Shiro 登陆认证 SimpleAuthenticationInfo

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 登陆认证 SimpleAuthenticationInfo)