美文网首页
Shiro 登陆认证 SimpleAuthenticationI

Shiro 登陆认证 SimpleAuthenticationI

作者: 白驹过隙_忽然而已 | 来源:发表于2020-12-01 16:22 被阅读0次

    1.shiro组件 Realm

    使用shiro框架需要自己定义一个Realm来进行登陆信息以及权限信息的认证。可以看作是shiro与数据库的桥梁。自定义Realm需要继承AuthorizingRealm 需要重写两个方法。

        //权限验证
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
            return null;
        }
    
        /**
         * 登陆认证,失败的话返回null
         *
         * @param authenticationToken
         * @return
         * @throws AuthenticationException
         */
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
            UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) authenticationToken;
            //密码
            String password = String.copyValueOf(usernamePasswordToken.getPassword());
    
            //用户名
            String principal = usernamePasswordToken.getUsername();
    
            SysUser user = sysUserService.getByName(principal);
            if (Objects.isNull(user)) {
                return null;
            }
    
            /**
             * 第一个参数 getPrincipal()获得,自己按需要传
             * 第二个参数 是用来和authenticationToken里的密码比对
             */
            SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(user,user.getPass(), this.getName());
            return simpleAuthenticationInfo;
        }
    

    可以看到doGetAuthenticationInfo方法是通过查询数据库的用户信息,返回一个SimpleAuthenticationInfo来实现登陆信息认证。具体是通过什么机制来通过这个返回对象与登陆信息认证,跟踪代码。入口是subject.login(token)方法,流程长就不一一贴上,定位到关键部分:

        protected void assertCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) throws AuthenticationException {
            CredentialsMatcher cm = this.getCredentialsMatcher();
            if (cm != null) {
                if (!cm.doCredentialsMatch(token, info)) {
                    String msg = "Submitted credentials for token [" + token + "] did not match the expected credentials.";
                    throw new IncorrectCredentialsException(msg);
                }
            } else {
                throw new AuthenticationException("A CredentialsMatcher must be configured in order to verify credentials during authentication.  If you do not wish for credentials to be examined, you can configure an " + AllowAllCredentialsMatcher.class.getName() + " instance.");
            }
        }
    

    这是AuthenticatingRealm的一个方法,入参示是登陆时前端传过来的登陆信息和我们自定义Realm返回的信息。通过 this.getredentialMatcher() 获取到一个CredentialsMatcher对象,如果我们不设置的话,默认是使用的SimpleCredentialsMatcher。进入到doCredentialsMatch方法:

        protected boolean equals(Object tokenCredentials, Object accountCredentials) {
            if (log.isDebugEnabled()) {
                log.debug("Performing credentials equality check for tokenCredentials of type [" + tokenCredentials.getClass().getName() + " and accountCredentials of type [" + accountCredentials.getClass().getName() + "]");
            }
    
            if (this.isByteSource(tokenCredentials) && this.isByteSource(accountCredentials)) {
                if (log.isDebugEnabled()) {
                    log.debug("Both credentials arguments can be easily converted to byte arrays.  Performing array equals comparison");
                }
    
                byte[] tokenBytes = this.toBytes(tokenCredentials);
                byte[] accountBytes = this.toBytes(accountCredentials);
                return MessageDigest.isEqual(tokenBytes, accountBytes);
            } else {
                return accountCredentials.equals(tokenCredentials);
            }
        }
    
        public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
            Object tokenCredentials = this.getCredentials(token);
            Object accountCredentials = this.getCredentials(info);
            return this.equals(tokenCredentials, accountCredentials);
        }
    

    这里就是匹配密码是否匹配的具体逻辑。
    我项目数据库是存储的加密的密码,使用的是BCryptPasswordEncoder来进行hash加密的,是需要用他的BCryptPasswordEncoder::matches(String plainPass,String encodePass) 来验证密码,所以可以自己重写一个matcher,代码如下:

    public class CustomerCredentialsMatcher implements CredentialsMatcher {
        
        PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
        
        @Override
        public boolean doCredentialsMatch(AuthenticationToken authenticationToken, AuthenticationInfo authenticationInfo) {
            //前端传来的密码
            String currentPass = String.copyValueOf((char[]) authenticationToken.getCredentials());
            //数据库密码
            String dbPass = (String) authenticationInfo.getCredentials();
            return passwordEncoder.matches(currentPass,dbPass);
        }
        
    }
    

    在自己的Realm里设置一下我们自己的matcher:

    public class CustRealm extends AuthorizingRealm {
    ...
        @Override
        public void setCredentialsMatcher(CredentialsMatcher credentialsMatcher) {
            CustomerCredentialsMatcher customerCredentialsMatcher = new CustomerCredentialsMatcher();
            super.setCredentialsMatcher(customerCredentialsMatcher);
        }
    ...
    }
    

    done.

    相关文章

      网友评论

          本文标题:Shiro 登陆认证 SimpleAuthenticationI

          本文链接:https://www.haomeiwen.com/subject/wiuswktx.html