Spring Boot「24」Shiro 核心模块分析

90 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 09 天,点击查看活动详情

SecurityManager 是 Shiro 的核心,它将 Authenticator、Authorizer 和 SessionManager 聚合在一起。 今天,我将分别介绍下 Shiro 中这几个模块的实现细节,帮助你更深入理解 Shiro 的工作机制。

首先,用 Apache 官网提供的架构图来了解下 Shiro 的整体架构:

img_shiro_archi.png

01-Authenticator

Authenticator 负责处理 Subject(用户)的登录请求。 因此,它需要具备具备读取用户认证信息(通过 Realm)的能力。

Authenticator 中只定义了一个方法,AuthenticationInfo authenticate(AuthenticationToken authenticationToken)org.apache.shiro.authc.AbstractAuthenticator 是 Authenticator 的一个基础实现,它提供了 authenticate 的模板方法实现(如下为简化版):

AuthenticationInfo info;
try {
    // 模板方法,具体逻辑留给派生类去实现
    info = doAuthenticate(token);
} catch (Throwable t){
    // 验证失败后,通知 listeners
    notifyFailure(token, t);
    throw t;
}
// 验证通过后,通知 listeners
notifySuccess(token, info);

Shiro 中提供了 AbstractAuthenticator 的唯一实现类 org.apache.shiro.authc.pam.ModularRealmAuthenticator,它提供的 doAuthenticate 方法实现为:

Collection<Realm> realms = getRealms();
if (realms.size() == 1) {
    // 如果只有一个 Realm,则返回 Realm 的验证结果
    return doSingleRealmAuthentication(realms.iterator().next(), authenticationToken);
} else {
    // 如果有多个 Realm,则根据 AuthenticationStrategy 聚合所有 Realm 验证结果后返回
    return doMultiRealmAuthentication(realms, authenticationToken);
}

AuthenticationStrategy 是与 Authenticator 相关的类,主要在使用多个 Realm 时,用来组合验证结果。 AuthenticationStrategy 的结构层次如下图所示: img_shiro_auth-strategy-archi.png 其中:

  • AllSuccessfulStrategy,指所有 Realm 中的验证结构都为 true,最终结果才为 true。
  • FirstSuccessfulStrategy,指 Realm 列表中的第一个 Realm 的验证结果为 true,最终结果为 true。
  • AtLeastOneSuccessfulStrategy,指 Realm 列表中至少有一个 Realm 的验证结果为 true,最终结果为 true。

02-Authorizer

Authorizer 负责验证用户的角色、权限等信息。 严格意义上说,Shiro 中只有一个 Authorizer 实现 org.apache.shiro.authz.ModularRealmAuthorizer。 虽然 SecurityManager、AuthorizingRealm 及其子类也实现了 Authorizer 接口,但它们的实现方式是将处理交给内部持有的 ModularRealmAuthorizer 对象来处理。

Authorizer 与 Authenticator 一样,需要与 Realms 进行交互,来获得用户的角色、权限信息。 另外两个与 Authorizer 关联的类型是 PermissionResolver 和 RolePermissionResolver,负责解析生成用户的权限。

03-Realm

Realm 接口相关的类的结构图如下所示: img_shiro_realm-archi.png

Realm 中有两个关键的接口:

  • boolean supports(AuthenticationToken token);
  • AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException;

这两个接口在 org.apache.shiro.realm.AuthenticatingRealm 中实现:

public boolean supports(AuthenticationToken token) {
    // token 是 authenticationTokenClass 类或子类 
    return token != null && getAuthenticationTokenClass().isAssignableFrom(token.getClass());
}

简化后:

public final AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {

    AuthenticationInfo info = getCachedAuthenticationInfo(token);
    if (info == null) {
        // 缓存中没有,则执行验证,并缓存
        info = doGetAuthenticationInfo(token);   // 更具体的业务相关 Realm 需要实现的方法
        if (token != null && info != null) {
            cacheAuthenticationInfoIfPossible(token, info);
        }
    }

    if (info != null) {
        // 验证,例如验证密码 PasswordMatcher
        assertCredentialsMatch(token, info);  
    } 
    return info;
}

两个具体的实现:

  • 基于文本的实现,org.apache.shiro.realm.text.TextConfigurationRealm
  • 基于 JDBC 的实现,org.apache.shiro.realm.jdbc.JdbcRealm

04-SessionManager

负责 Session 的管理。 Shiro 中 Session 的设计结构为如下图所示: img_shiro_session-archi.png 其中,Web 应用中,默认使用 HttpServletSession,它仅仅是对 javax.servlet.http.HttpSession 的包装。

SessionFactory 负责创建 Session。 Shiro 中仅有一个默认实现 org.apache.shiro.session.mgt.SimpleSessionFactory,返回 SimpleSession 对象。 可以根据业务需要,定义并实现 SessionFactory。

SessionDAO 负责 Session 对象的持久化。 它定义了对持久化层(例如文件系统、Redis、其他 EIS 系统)中 Session 对象的“增删改查”操作。 它的设计结构如下图所示: img_shiro_session-dao-archi.png 如上所示,Shiro 提供了以内存、缓存管理器实现的 SessionDAO。 我们也可以据此实现基于 Redis 的实现。

SessionManager 将上述三部分整合在一起,负责 Session 的管理。 img_shiro_session-manager-archi.png

05-总结

今天,我介绍了 Shiro 中的核心模块及它们的架构设计,帮助理解 Shiro 的工作原理非常有必要。 希望今天的内容对你有所帮助。