开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 09 天,点击查看活动详情
SecurityManager 是 Shiro 的核心,它将 Authenticator、Authorizer 和 SessionManager 聚合在一起。 今天,我将分别介绍下 Shiro 中这几个模块的实现细节,帮助你更深入理解 Shiro 的工作机制。
首先,用 Apache 官网提供的架构图来了解下 Shiro 的整体架构:
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 的结构层次如下图所示:
其中:
- 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 接口相关的类的结构图如下所示:
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 的设计结构为如下图所示:
其中,Web 应用中,默认使用 HttpServletSession,它仅仅是对
javax.servlet.http.HttpSession
的包装。
SessionFactory 负责创建 Session。
Shiro 中仅有一个默认实现 org.apache.shiro.session.mgt.SimpleSessionFactory
,返回 SimpleSession 对象。
可以根据业务需要,定义并实现 SessionFactory。
SessionDAO 负责 Session 对象的持久化。
它定义了对持久化层(例如文件系统、Redis、其他 EIS 系统)中 Session 对象的“增删改查”操作。
它的设计结构如下图所示:
如上所示,Shiro 提供了以内存、缓存管理器实现的 SessionDAO。
我们也可以据此实现基于 Redis 的实现。
SessionManager 将上述三部分整合在一起,负责 Session 的管理。
05-总结
今天,我介绍了 Shiro 中的核心模块及它们的架构设计,帮助理解 Shiro 的工作原理非常有必要。 希望今天的内容对你有所帮助。