Apache Shiro 框架详解
文章目录
- 一、Shiro 核心功能
- 二、Shiro 架构
- 2.1 三层架构
- 2.2 核心组件(SecurityManager 内部)
- 三、核心流程详解
- 3.1 认证流程(登录)
- 流程步骤:
- 认证流程序列图:
- 3.2 授权流程(权限校验)
- 流程步骤:
- 授权流程序列图:
- 3.3 会话管理
- 1. 会话管理核心组件
- 2. 会话管理流程
- 3. 关键特性
- 3.4 加密
- 1. 核心加密组件
- 2. 密码加密与验证流程
- 3. 关于哈希和盐值的补充说明
- 3.5 其他核心功能
- 1. 缓存(Caching)
- 2. rememberMe(记住我)
- 3. 并发登录控制
- 四、Shiro 核心组件详解
- 4.1 Subject
- 4.2 SecurityManager
- 4.3 Realm
- 五、Shiro 与 Web 集成
- 核心配置(web.xml):
- URL 权限配置(shiro.ini):
- 六、同类型产品分析
- 主流方案对比:Shiro vs Spring Security
一、Shiro 核心功能
Shiro 的核心功能可以概括为 “认证、授权、会话管理、加密”,此外还提供了缓存、RememberMe 等辅助功能。
- 认证(Authentication):验证用户身份(“你是谁?”),例如登录时校验用户名和密码。
- 授权(Authorization):验证用户权限(“你能做什么?”),例如判断用户是否有权限访问某个接口或按钮。
- 会话管理(Session Management):管理用户会话(即使在非 Web 环境中也能使用),支持会话过期、会话存储等。
- 加密(Cryptography):提供安全的加密算法(如 MD5、SHA)和密码哈希功能,保护敏感数据。
- 其他功能:缓存(提升权限校验性能)、RememberMe(记住登录状态)、Web 集成、集群会话等。
二、Shiro 架构
Shiro 采用分层架构,从外部到内部分为 “Subject - SecurityManager - Realms” 三层,每层职责明确,且内部包含多个核心组件协同工作。
2.1 三层架构
- Subject:对外的“用户”抽象(可以是用户、进程等),所有操作都通过 Subject 触发(如登录、权限校验)。
- SecurityManager:Shiro 的核心,负责协调内部组件完成安全操作(如认证、授权),是 Shiro 的“大脑”。
- Realms:连接应用与数据源的桥梁,用于获取用户信息(用户名/密码)和权限信息(角色/权限),Shiro 通过 Realms 验证身份和权限。
2.2 核心组件(SecurityManager 内部)
SecurityManager 内部包含多个组件,分工处理不同的安全功能:
- Authenticator:负责认证流程(如校验用户名密码),默认实现为
ModularRealmAuthenticator
,支持多 Realm 认证。 - Authorizer:负责授权流程(如校验用户是否有某权限),默认实现为
ModularRealmAuthorizer
。 - SessionManager:管理用户会话,不依赖 Web 容器(如 Tomcat),支持在非 Web 环境(如 Swing 应用)中使用。
- CacheManager:缓存用户信息、权限数据等,减少重复查询数据源的开销(默认支持 EhCache、Redis 等)。
- Cryptography:提供加密工具类(如
SimpleHash
),支持 MD5、SHA 等哈希算法,避免明文存储密码。
SecurityManager 内部维护了一个或多个 Realm 实例。 SecurityManager 下管理的认证器、授权器会直接与 Realms 交互,其他组件(如 SessionManager、CacheManager 等)不会直接访问 Realms。
三、核心流程详解
3.1 认证流程(登录)
认证流程是验证用户身份的过程,核心是校验“用户提供的凭证(如密码)”与“数据源中的凭证”是否一致。
流程步骤:
- 用户通过 Subject 提交认证请求(如输入用户名密码)。
- Subject 将请求委托给 SecurityManager。
- SecurityManager 委托 Authenticator 处理认证。
- Authenticator 通过 Realm 从数据源获取用户信息(如数据库中的密码)。
- Authenticator 比对用户提交的凭证与数据源中的凭证,返回认证结果。
认证流程序列图:
3.2 授权流程(权限校验)
授权流程是验证用户是否有权限执行某个操作的过程,核心是检查“用户拥有的权限”是否包含“操作所需的权限”。
流程步骤:
- 用户通过 Subject 发起权限校验请求(如访问某接口)。
- Subject 将请求委托给 SecurityManager。
- SecurityManager 委托 Authorizer 处理授权。
- Authorizer 通过 Realm 从数据源获取用户的权限信息(角色/权限)。
- Authorizer 校验用户权限是否满足操作需求,返回授权结果。
授权流程序列图:
3.3 会话管理
Shiro 的会话管理是其核心功能之一,支持跨环境(Web/非Web)的会话统一管理,无需依赖容器(如Tomcat)的会话机制,且提供丰富的会话控制能力。
1. 会话管理核心组件
- SessionManager:管理所有会话的生命周期(创建、读取、更新、删除),默认实现为
DefaultSessionManager
(非Web环境)和ServletContainerSessionManager
(Web环境,依赖容器会话),推荐自定义DefaultWebSessionManager
实现独立管理。 - SessionDAO:负责会话的持久化(如内存、数据库、Redis等),常用实现:
MemorySessionDAO
:默认内存存储(适合开发环境)。EnterpriseCacheSessionDAO
:缓存存储(结合EhCache/Redis)。- 自定义DAO:集成Redis实现分布式会话(解决集群会话共享问题)。
- SessionListener:监听会话创建、过期、停止等事件(如记录用户登录日志)。
- SessionValidationScheduler:定期验证会话有效性,清理过期会话(默认每30分钟执行一次)。
2. 会话管理流程
3. 关键特性
- 会话标识(Session ID):自动生成唯一ID,可通过
session.getId()
获取,常用于跟踪用户会话。 - 会话属性:支持存储自定义属性(如
session.setAttribute("user", userInfo)
),实现用户信息跨请求共享。 - 过期控制:可设置全局过期时间(如30分钟无操作失效)或单个会话过期时间(
session.setTimeout(3600000)
)。 - 分布式支持:通过自定义
SessionDAO
集成Redis,实现多服务器间会话共享(解决集群环境下登录状态同步问题)。
3.4 加密
Shiro 提供简化的加密工具,封装了常见加密算法,避免直接操作复杂的Java加密API,常用于密码加密存储。
1. 核心加密组件
- HashService:提供哈希计算(如MD5、SHA-256),
SimpleHash
是常用实现,需指定算法、原始数据、盐值、哈希次数。 - 盐值(Salt):随机生成的字符串,与密码混合后哈希(防止彩虹表破解,如
salt = new SecureRandomNumberGenerator().nextBytes()
)。 - CredentialsMatcher:验证用户提交的凭证(如密码)与存储的哈希值是否匹配(如
HashedCredentialsMatcher
)。
2. 密码加密与验证流程
3. 关于哈希和盐值的补充说明
在Shiro等安全框架中,哈希(Hash) 和盐值(Salt) 是保护密码的核心技术,用于将明文密码转换为不可逆的密文,同时抵御常见的破解手段。下面通过原理拆解和流程示例,清晰说明它们的作用:
-
哈希(Hash):将密码"单向转换"为密文
哈希算法(如MD5、SHA-256、Shiro中的SimpleHash
)是一种单向加密函数,它能将任意长度的输入(如明文密码)转换为固定长度的输出(哈希值,即密文),且无法从哈希值反推回明文。 -
哈希的核心作用:
- 不可逆性 :
例如,明文"123456"
通过SHA-256哈希后可能得到"8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92"
,但无法从这个哈希值反算出"123456"
。这意味着即使数据库泄露,攻击者也无法直接获取明文密码。 - 固定长度输出:
无论输入是1个字符还是1000个字符,哈希值长度固定(如SHA-256始终输出64个字符),避免密码长度泄露。 - 雪崩效应:
输入的微小变化(如密码从"123456"
改为"123457"
)会导致哈希值完全不同,进一步增强安全性。
- 不可逆性 :
-
盐值(Salt):解决"相同密码哈希值相同"的漏洞
哈希算法有一个问题:相同的明文会生成完全相同的哈希值。例如,两个用户都用"123456"
作为密码,它们的哈希值完全一样。这会导致两个风险:- 攻击者可通过"彩虹表"(预计算的常见密码哈希值字典)快速匹配出明文;
- 一旦一个用户密码被破解,所有使用相同密码的用户都会暴露。
-
盐值的作用:给每个密码添加一个随机唯一的字符串(盐值),再进行哈希,确保即使明文相同,最终的哈希值也不同。
- 盐值的特性:
- 随机性:每个用户的盐值独立生成,不可预测;
- 唯一性:通常与用户账号绑定(如存储在用户表中),确保同一用户的盐值固定;
- 公开性:盐值不需要保密,可随哈希值一起存储(因为破解难度不在盐值本身,而在加盐后的哈希计算)。
- 盐值的特性:
-
为什么哈希+盐值能有效防破解?
- 抵御彩虹表攻击:
彩虹表是预计算的"明文-哈希值"对应表,但加盐后,相同明文的哈希值完全不同,攻击者需要为每个可能的盐值生成彩虹表,计算量呈指数级增长,几乎不可能实现。 - 防止批量破解:
即使两个用户密码相同,由于盐值不同,哈希值也不同,破解一个用户的密码不会影响其他用户。 - 增加暴力破解难度:
暴力破解(逐个尝试明文并计算哈希)的效率极低,尤其是配合"哈希迭代次数"(如Shiro的hashIterations
,重复多次哈希计算),可进一步增加破解时间。
- 抵御彩虹表攻击:
3.5 其他核心功能
1. 缓存(Caching)
- 作用:缓存用户权限信息(如角色、权限),减少数据库查询,提升性能。
- 核心组件:
CacheManager
:管理缓存(默认集成EhCache,可替换为RedisCacheManager)。AuthorizingRealm
:通过doGetAuthorizationInfo
加载权限时自动缓存,默认缓存时间由缓存管理器配置。
- 使用场景:用户登录后,权限信息被缓存,后续授权操作直接从缓存获取,直至缓存过期或用户权限更新。
2. rememberMe(记住我)
- 功能:用户勾选“记住我”后,关闭浏览器再打开,无需重新登录即可访问非敏感资源。
- 实现原理:
- 登录成功后,Shiro生成加密的
rememberMe
Cookie(包含用户标识),存储在客户端。 - 下次访问时,Shiro读取Cookie并自动登录(但
subject.isAuthenticated()
为false
,subject.isRemembered()
为true
)。
- 登录成功后,Shiro生成加密的
- 安全控制:敏感操作(如支付)需强制要求用户重新认证(
subject.isAuthenticated() == true
)。
3. 并发登录控制
- 功能:限制同一用户同时登录的设备数量(如只允许一个设备在线)。
- 实现方式:
- 自定义
SessionListener
,登录时检查该用户的活跃会话数量。 - 超过限制时,强制踢出旧会话(
oldSession.stop()
)。
- 自定义
四、Shiro 核心组件详解
4.1 Subject
- 定义:代表当前“用户”(可以是真实用户、进程、服务等),是开发者与 Shiro 交互的入口。
- 常用方法:
login(AuthenticationToken token)
:登录(提交认证)。isAuthenticated()
:判断是否已认证(登录成功)。hasRole(String role)
:判断是否拥有某个角色。checkPermission(String permission)
:校验是否拥有某个权限(无权限时抛异常)。getSession()
:获取当前会话。
4.2 SecurityManager
- 定义:Shiro 的核心管理器,协调所有组件(认证、授权、会话等),开发者需先初始化并配置它。
- 初始化示例(通过 Ini 配置文件):
// 读取 Shiro 配置文件 Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini"); SecurityManager securityManager = factory.getInstance(); // 设置全局 SecurityManager SecurityUtils.setSecurityManager(securityManager);
4.3 Realm
-
定义:连接应用与数据源的桥梁,负责提供用户信息(认证)和权限信息(授权)。
-
常见类型:
IniRealm
:从 ini 配置文件读取用户/权限数据(适合简单场景)。JdbcRealm
:从数据库读取数据(需配置 SQL 语句)。- 自定义 Realm:继承
AuthorizingRealm
,重写认证和授权方法(适合复杂业务)。
-
自定义 Realm 示例:
public class MyRealm extends AuthorizingRealm {// 授权:获取用户权限@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {String username = (String) principals.getPrimaryPrincipal();// 从数据库查询用户权限(示例)Set<String> permissions = new HashSet<>();permissions.add("user:query");SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();info.setStringPermissions(permissions);return info;}// 认证:获取用户凭证@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {String username = (String) token.getPrincipal();// 从数据库查询密码(示例)String password = "123456"; // 实际应从 DB 读取return new SimpleAuthenticationInfo(username, password, getName());} }
五、Shiro 与 Web 集成
在 Web 应用中,Shiro 通过 Filter 拦截请求,实现 URL 级别的权限控制。
核心配置(web.xml):
<!-- Shiro 过滤器 -->
<filter><filter-name>shiroFilter</filter-name><filter-class>org.apache.shiro.web.servlet.ShiroFilter</filter-class>
</filter>
<filter-mapping><filter-name>shiroFilter</filter-name><url-pattern>/*</url-pattern>
</filter-mapping>
URL 权限配置(shiro.ini):
[urls]
/login = anon # 登录页允许匿名访问
/logout = logout # 退出登录
/user/** = authc, perms["user:manage"] # /user/** 路径需认证且拥有 user:manage 权限
/admin/** = authc, roles["admin"] # /admin/** 路径需认证且拥有 admin 角色
六、同类型产品分析
在Java生态中,主流的安全框架以 Apache Shiro 和 Spring Security 为主,此外还有针对特定场景的解决方案(如Keycloak、Pac4j等)。选择框架时需结合项目规模、技术栈、安全需求复杂度等因素。
主流方案对比:Shiro vs Spring Security
两者是最常用的企业级安全框架,核心功能覆盖认证、授权、加密等,但设计理念和适用场景差异显著。
对比维度 | Apache Shiro | Spring Security |
---|---|---|
设计理念 | 简洁、易用,强调“开箱即用”,API直观易懂 | 功能全面,强调“深度集成Spring生态”,灵活性强 |
核心功能 | 认证、授权、会话管理、加密、缓存集成 | 认证、授权、OAuth2.0/OpenID Connect、SSO、LDAP等(功能更全) |
易用性 | 学习曲线平缓,配置简单(XML/注解均可),文档清晰 | 学习曲线较陡,配置复杂(依赖Spring生态知识) |
集成性 | 与Spring、Java EE、Servlet、JAX-RS等均可集成(不绑定特定框架) | 深度绑定Spring生态(Spring Boot/Cloud无缝集成),非Spring项目集成较麻烦 |
会话管理 | 内置独立会话管理(不依赖Servlet容器),支持分布式会话 | 早期依赖Servlet容器会话,需配合Spring Session实现分布式 |
扩展能力 | 扩展点明确(如Realm、Filter),适合简单扩展 | 扩展点极多(如SecurityContext、AuthenticationProvider),适合复杂场景定制 |
适用场景 | 中小型应用、快速开发、多框架集成、非Spring项目 | 大型企业应用、Spring生态项目、复杂安全需求(如SSO、OAuth2) |
社区活跃度 | 社区稳定,更新频率中等(2023年发布1.12.0版本) | 社区活跃,更新频繁(依赖Spring版本迭代) |
Shiro是“简单场景的最优解”,以低学习成本满足基础安全需求;Spring Security是“复杂场景的全能选手”,适合深度集成Spring生态的大型项目。选型时不必纠结“功能多少”,而应聚焦“项目实际需求”和“团队技术栈匹配度”——合适的才是最好的。