shiro登录认证

java

浏览数:888

2019-1-7

片段 1片段 2片段 3片段 4片段 5片段 6


1、代码:javaConfig shiro环境

public class ConfigShiro {

    public void simpleDbCOnfig(){
        DefaultSecurityManager securityManager = 
                new DefaultSecurityManager();
        
        Realm realm = new MyRealm();
        securityManager.setRealm(realm);
        SecurityUtils.setSecurityManager(securityManager);
    }
    
}


2、代码:生成盐,存到数据库中,salt我们也存到数据库中,解密时会取出

public void saltTest(){
        //所需加密的参数  即  密码
        String pwd = "123";
        //[盐] 一般为用户名 或 随机数
        String salt = "shiro";
        //加密次数
        int hashIterations = 1;

        /*调用org.apache.shiro.crypto.hash.SimpleHash.SimpleHash(String algorithmName, Object source, Object salt, int hashIterations)
         * 构造方法实现盐值加密  String algorithmName 为加密算法 支持md5 base64 等*/
        SimpleHash sh = new SimpleHash("md5", pwd, salt, hashIterations);
        //打印最终结果,或将其存入数据库
        System.out.println(sh.toHex());
    }


3、代码:MyRealm(加盐)

package com.amiu.shiro.chapter5;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authc.credential.CredentialsMatcher;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;

import com.amiu.mybatisTest.autoMybatis.SqlSessionHelper;
import com.amiu.shiro.db.User;
import com.amiu.shiro.db.UsersDao;

public class MyRealm extends AuthorizingRealm {
    //操作数据库的类
    @Autowired
    RoleService roleService;
    @Autowired
    PermissionService permissionService;
    @Autowired 
    UserDao userDao;
    
    //设置盐解析,这里要和生成盐的设置相同,使用MD5,解密次数1次
    @Override
    public void setCredentialsMatcher(CredentialsMatcher credentialsMatcher) {
        //HashedCredentialsMatcher是shiro提供的解析盐的实现类 
        HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
        matcher.setHashAlgorithmName("md5");
        matcher.setHashIterations(1);
        super.setCredentialsMatcher(matcher);
    }
    
    //处理权限的,将角色Role和权限Permission存入shiro中
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(
            PrincipalCollection principals) {
        // 获取当前登录对象
        User user = (User) principals.getPrimaryPrincipal();
        //查询拥有角色
        Map<Long, Role> mapRole = roleService.findRoleByUserId(user.getId());
        
        List<Long> roleIds = MapUtil.toListKey(mapRole);
        //查询角色拥有的权限
        Map<Long, Permission> mapPermission = 
                permissionService.findByRoleId(roleIds);
        
        Set<String> roles = null;
        Set<String> stringPermissions = null;
        try {
            //将需要的字段转换为Set格式,name是实体类Role的字段
            roles = MapUtil.toSet(mapRole, "name");
            stringPermissions = MapUtil.toSet(mapPermission, "name") ;
        } catch (Exception e) {
            //这里是MapUtil工具类捕捉的异常
            e.printStackTrace();
        }
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        //将角色与权限放入SimpleAuthorizationInfo对象
        authorizationInfo.setRoles(roles);
        authorizationInfo.setStringPermissions(stringPermissions);
        //返回给shiro
        return authorizationInfo;
    }
    //处理身份验证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(
            AuthenticationToken token) throws AuthenticationException {
        UsernamePasswordToken userToken = (UsernamePasswordToken) token;
        //获取当前需要登录的用户
        String name = userToken.getUsername();
        //从数据库获取是否有对应的用户
        User user = userDao.selectByName(name);
        
        if(user == null){
            //不存在用户
            throw new UnknownAccountException();
        }
        //从数据库中获取账户锁定信息
        if(user.isIs_lock()){
            //账户被锁定
            throw new LockedAccountException();
        }
        //从数据库中取出盐
        String salt = user.getPassword_salt();
        //从数据库中取出密码,密码是加过盐的
        String password = user.getPassword();
        //转换为byte类型的盐
        ByteSource byteSalt = ByteSource.Util.bytes(salt);
        
        //返回对象,上面我们设置过的盐解析类:HashedCredentialsMatcher
        //会帮我们解析盐,然后验证密码是否匹配
        SimpleAuthenticationInfo info = 
                new SimpleAuthenticationInfo(user,
                        password.toCharArray(), byteSalt, getName());
        
        return info;
    }

    @Override
    public String getName() {
        return "myRealm";
    }

}


4、代码:登陆测试

    @Test
    public void saltLogin(){
        new ConfigShiro().simpleDbCOnfig();
        Subject subject = SecurityUtils.getSubject();
        //页面用户“li”输入的密码是明文“123”
        UsernamePasswordToken token = 
                new UsernamePasswordToken("li","123");
        try {
            subject.login(token);
        } catch (UnknownAccountException unknownAccountEx) {
            //处理无此用户
        }catch(IncorrectCredentialsException wrongPasswordEx){
            //处理用户名或密码不正确
        }catch(LockedAccountException lockedAccountEx){
            //账户被锁定
        } catch ( AuthenticationException ae ) {
            //不期望出现的错误  error?
        }
        
        Assert.assertTrue(subject.isAuthenticated());
    }


5、解析:MyRealm

1、登陆测试中的:
    UsernamePasswordToken token = new UsernamePasswordToken("li","123");

执行subject.login(token)后,这个token传到了MyRealm中的方法:
    //处理身份验证
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token)
这里的参数(AuthenticationToken token)即是我们的登陆用户“li”的token

2、我们自定义的Realm,我们一般选择继承shiro的AuthorizingRealm。

3、MyRealm中的返回值:
    SimpleAuthenticationInfo info = 
                new SimpleAuthenticationInfo(user,
                        password.toCharArray(), byteSalt, getName());
SimpleAuthenticationInfo的第一个参数我么可以放入我们想要放入才参数,如这里的对象User,我们还能存入id
或Username等等,这个参数体现在本类处理权限的方法中:
    //处理权限
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals)
这里的参数(PrincipalCollection principals)就是我们传入的对象User,我们可以这么使用它:
    User User = (User) principals.getPrimaryPrincipal();
获取User对象后我们可以去数据库中查询权限信息,并加载到shiro中


6、解析:MyRealm中的重写方法setCredentialsMatcher()

@Override
public void setCredentialsMatcher(CredentialsMatcher credentialsMatcher) {
    super.setCredentialsMatcher(credentialsMatcher);
}

1、CredentialsMatcher字面意思简单理解就是[用户匹配器],作用就是匹配密码,检查
   [登录密码]是否等于[数据库中查询的密码]

2、CredentialsMatcher的默认实现是SimpleCredentialsMatcher,作用是简单匹配密码,
   当匹配[登录密码:123],[数据库中查询的密码:123]时,才会成功。
   当匹配[登录密码:123],[数据库中加密的密码:1f45e9afefc5e28edbcbdfae08a1f7a6]时,
   是不会成功的;SimpleCredentialsMatcher作用其实也就是简单判等。
   上面的setCredentialsMatcher()等同于:
   @Override
    public void setCredentialsMatcher(CredentialsMatcher credentialsMatcher) {
        SimpleCredentialsMatcher matcher = new SimpleCredentialsMatcher();
        super.setCredentialsMatcher(matcher);
    }

3、当我们需要匹配加盐的密码时,就不能使用SimpleCredentialsMatcher了,我们一般使用
   HashedCredentialsMatcher,HashedCredentialsMatcher可以匹配[登录明文密码]与[数据库加密密码]
   但是我们必须为其指定匹配规则,也就是密码加密时的加密规则,HashedCredentialsMatcher将按照
   此规则解密[数据库加密密码],然后再匹配[登录明文密码],如:
    @Override
    public void setCredentialsMatcher(CredentialsMatcher credentialsMatcher) {
        HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
        matcher.setHashAlgorithmName("md5");//设置解密算法为:md5
        matcher.setHashIterations(1);//设置解密次数为1次
        super.setCredentialsMatcher(matcher);
    }