News:三分天注定,七分靠打拼,爱拼才会赢!致力打造专业IT博客。如果你对本博客有任何意见或建议请联系作者,邮箱:blog@caokuan.cn

关于对 Shiro 框架中 Realm 的认证过程的一些理解

逝水无痕 247 0 条

Realm 在 Shiro 中起着非常重要的作用,主要用来进行认证和授权(设置登录用户的角色和权限等信息),下面就是我对项目中 Realm 的认证过程的一些理解。

shiro.png

项目在集成 Shiro 框架时需要实现一个自定义的 Realm ,通常是继承 AuthorizingRealm ,并覆写两个方法:doGetAuthenticationInfo 和 doGetAuthorizationInfo。 下面是示例代码展现:

public class MyShiroRealm extends AuthorizingRealm {
    /**
     * 认证  登录
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        UsernamePasswordToken userToken = (UsernamePasswordToken) token;
        final String userName = userToken.getUsername(); // 获取要登录的用户名
        User user = userService.findByUserName(userName); // 调用服务查询用户
        final String password = user.getPassword(); // 拿到该用户的密码
        // 构造 SimpleAuthenticationInfo 并返回
        return new SimpleAuthenticationInfo(userName, password, this.getClass().getName());
    }
    /**
     * 授权  角色  权限
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        // 此处省略关于角色、权限相关的代码
    }
}

以上代码为示例代码,仅用于举例,在实际项目中需要有完善的判断逻辑。

那么 Shiro 是如何调用自定义的 Realm 来进行认证的呢?

通过阅读源码可以看到如下的调用关系:

SecurityUtils.getSubject().login(AuthenticationToken) 

--> SecurityManager.login() 

--> AuthenticatingSecurityManager.authenticate() 

--> Authenticator.authenticate() 

--> Authenticator.doAuthenticate() 

--> Realm.getAuthenticationInfo() 

--> MyShiroRealm.doGetAuthenticationInfo() 

经过一系列的调用,最终到自定义的 doGetAuthenticationInfo() 方法

上面所说的这一列调用说明了程序最终会调用到我们自定义的Realm,那么下面继续说明是在哪一步进行密码比较来完成认证的:

在倒数第二步的 Realm.getAuthenticationInfo() 源码中(省略了部分非核心代码)可以看到:

public final AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
    // 下面是获取 AuthenticationInfo ,就是从自定义的 MyShiroRealm 的 doGetAuthenticationInfo(token) 方法获得的
    // 如果使用了缓存,就先从缓存中获得
    AuthenticationInfo info = getCachedAuthenticationInfo(token);
    if (info == null) {
        info = doGetAuthenticationInfo(token);
        if (token != null && info != null) {
            cacheAuthenticationInfoIfPossible(token, info); // 放入缓存中
        }
    }
    if (info != null) {
        // 在这里调用匹配器进行密码比较
        // 匹配器可自定义
        assertCredentialsMatch(token, info);
    }
    return info;
}

对于 assertCredentialsMatch() 方法的实现如下:

protected void assertCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) throws AuthenticationException {
    // 通过配置可以在这里获取我们自定义的匹配器
    CredentialsMatcher cm = getCredentialsMatcher();
    if (cm != null) {
        if (!cm.doCredentialsMatch(token, info)) { // 在这里进行比较
            // 抛出匹配不成功的异常
        }
    } else {
        // 抛出没有配置匹配器异常
    }
}

假如我们使用 SimpleCredentialsMatcher 这个 CredentialsMatcher 实现类:

public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
    Object tokenCredentials = getCredentials(token);
    Object accountCredentials = getCredentials(info);
    return equals(tokenCredentials, accountCredentials);
}

可以看到 SimpleCredentialsMatcher 会去拿在Realm中获取的 AuthenticationInfo 中赋值的密码和在进行登录操作时传入的 AuthenticationToken 中设置的密码进行比较。

理解了这个基本过程,我们就可以通过自定义的配置来实现我们想要的认证方式了。

发表我的评论
icon_mrgreen.gificon_neutral.gificon_twisted.gificon_arrow.gificon_eek.gificon_smile.gificon_confused.gificon_cool.gificon_evil.gificon_biggrin.gificon_idea.gificon_redface.gificon_razz.gificon_rolleyes.gificon_wink.gificon_cry.gificon_surprised.gificon_lol.gificon_mad.gificon_sad.gificon_exclaim.gificon_question.gif

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址