权限认证-shiro相关原理以及Demo
连接安全框架与实际数据的桥梁。代表当前操作的用户(人、服务或设备),提供身份认证(login/logout)和权限检查(isPermitted/hasRole)等 API。加密支持:Shiro 提供 HashedCredentialsMatcher,支持盐值哈希(SHA-256、BCrypt)存储密码,避免明文泄露风险。它是线程安全的单例,充当内部组件的“总控中心”。获取当前用
一、核心架构:三层抽象模型
Shiro 的架构围绕三个核心组件构建,形成分层协作机制:
Subject(主体)
代表当前操作的用户(人、服务或设备),提供身份认证(login/logout)和权限检查(isPermitted/hasRole)等 API。开发者通过 Subject 接口与 Shiro 交互,无需直接操作底层组件。
示例代码:
Subject currentUser = SecurityUtils.getSubject();
currentUser.login(new UsernamePasswordToken("user", "pass")); // 认证
if (currentUser.hasRole("admin")) { ... } // 授权检查
SecurityManager(安全管理器)
安全操作的核心协调者,管理所有 Subject 的生命周期及安全策略(如认证、授权)。它是线程安全的单例,充当内部组件的“总控中心”。
配置示例(INI 方式):
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
SecurityUtils.setSecurityManager(factory.getInstance());
Realm(安全数据源)
连接安全框架与实际数据的桥梁。开发者需实现 Realm 接口,从数据库、LDAP 等来源加载用户身份及权限信息。Shiro 支持多 Realm 并行,通过 AuthenticationStrategy 控制认证策略(如首次成功或全部验证)。
关键方法:自定义重写实现
doGetAuthenticationInfo():验证用户身份(如密码比对)。
doGetAuthorizationInfo():获取用户的角色/权限数据。
二、实现原理详解
1. 认证流程(Authentication)
用户登录时,Shiro 执行标准化流程:
用户提交凭证(如用户名/密码)生成 AuthenticationToken。
Subject 委托 SecurityManager 进行认证。
SecurityManager 调用 Authenticator,后者通过 Realm 查询用户信息。
匹配凭证与 Realm 返回的 AuthenticationInfo(含密码哈希、盐值等)。
失败时抛出特定异常(如 UnknownAccountException、IncorrectCredentialsException)。
加密支持:Shiro 提供 HashedCredentialsMatcher,支持盐值哈希(SHA-256、BCrypt)存储密码,避免明文泄露风险。
2. 授权流程(Authorization)
权限检查分为两类:
基于角色的控制:hasRole(“admin”)
基于资源的控制:isPermitted(“user:delete:123”)(语法:资源:操作:实例)
执行逻辑:
调用 Subject.isPermitted() 触发授权请求。
SecurityManager 委派 Authorizer 比对用户权限与所需权限。
Authorizer 通过 Realm 加载权限数据,使用 PermissionResolver 解析权限字符串。
3. 会话管理(Session Management)
Shiro 抽象了独立于容器的 Session API,特点包括:
跨环境支持:在非 Web 应用(如消息队列)中管理用户状态。
分布式扩展:通过 SessionDAO 接口集成 Redis/Memcached 实现分布式会话。
生命周期控制:自动处理会话超时、失效事件。
4. 缓存机制
CacheManager(默认使用内存缓存)缓存 Realm 的授权数据,减少重复查询数据库的开销。开发者可替换为 EhCache、Redis 等实现。
三、扩展性与集成
1. 自定义组件
Realm:继承 AuthorizingRealm,重写认证/授权方法。
SessionDAO:实现会话持久化(如存入数据库)。
Cryptography:扩展加密算法(如国密 SM3)。
2. Web 集成
通过 ShiroFilter 拦截 URL,配置链式规则:
[urls]
/login = anon
/admin/** = authc, roles[admin]
/api/** = ssl, rest[user]
支持注解(如 @RequiresRoles)在 Servlet 或 Spring 中控制方法级权限。
3. 微服务适配
结合 OAuth2 Realm 实现单点登录(SSO),或使用 JWT 替代传统 Session。
四、核心组件功能总结
组件 职责 可扩展性
Subject 用户代理,提供安全操作入口 绑定自定义身份源
SecurityManager 协调认证、授权、会话等核心流程 配置策略(如多 Realm 顺序)
Realm 从数据源加载用户及权限数据 实现自定义数据库/API 查询逻辑
SessionManager 管理用户会话生命周期 支持分布式存储(如 Redis)
CacheManager 缓存权限数据提升性能 替换为第三方缓存实现
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.Subject;
public class ShiroDemo {
public static void main(String[] args) {
// 1. 创建安全管理器(Shiro 核心)
DefaultSecurityManager securityManager = new DefaultSecurityManager();
// 2. 设置自定义 Realm(替代数据库查询)
securityManager.setRealm(new CustomRealm());
// 3. 绑定 SecurityManager 到全局工具类
SecurityUtils.setSecurityManager(securityManager);
// 4. 获取当前用户主体
Subject currentUser = SecurityUtils.getSubject();
// 5. 模拟用户登录(用户名/密码)
UsernamePasswordToken token = new UsernamePasswordToken("admin", "123456");
try {
currentUser.login(token); // 触发认证流程
System.out.println("✅ 认证成功!用户:" + currentUser.getPrincipal());
// 6. 验证角色和权限
if (currentUser.hasRole("admin")) {
System.out.println("🛡️ 用户拥有 admin 角色");
}
if (currentUser.isPermitted("user:delete")) {
System.out.println("🔑 用户拥有删除权限");
}
} catch (UnknownAccountException e) {
System.out.println("❌ 用户名不存在");
} catch (IncorrectCredentialsException e) {
System.out.println("❌ 密码错误");
}
}
// 自定义 Realm(模拟数据库数据)
static class CustomRealm extends AuthorizingRealm {
// 认证逻辑:验证用户名密码
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) {
String username = (String) token.getPrincipal(); // 获取用户名
if (!"admin".equals(username)) {
throw new UnknownAccountException("用户不存在"); // [3,6](@ref)
}
// 模拟数据库密码(此处明文仅为示例,实际应存储哈希值)
String dbPassword = "123456";
return new SimpleAuthenticationInfo(username, dbPassword, getName()); // [3](@ref)
}
// 授权逻辑:返回用户角色/权限
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.addRole("admin"); // 添加角色
info.addStringPermission("user:delete"); // 添加权限(语法:资源:操作)[5](@ref)
return info;
}
}
}
关键步骤解析
初始化 SecurityManager
Shiro 的核心控制器,管理所有安全操作。
自定义 Realm
doGetAuthenticationInfo:验证用户身份(模拟数据库查询)
doGetAuthorizationInfo:返回用户角色和权限(如 user:delete 表示删除权限)。
用户登录流程
创建 UsernamePasswordToken 封装凭证
调用 subject.login(token) 触发 Realm 认证方法。
权限验证
hasRole(“admin”):检查角色
isPermitted(“user:delete”):检查细粒度权限。
注解形式权限原理:
2. AOP 动态拦截
当方法被调用时,代理对象(由 Spring AOP 或 AspectJ 生成) 会先拦截执行。Shiro 通过切面类(如 ShiroAnnotationAuthorizingAspect)在方法执行前插入权限校验逻辑。
拦截时机:方法调用前(@Before 增强)。
拦截逻辑:
public void beforeMethod(JoinPoint joinPoint) {
// 1. 解析方法上的注解(如 @RequiresPermissions)
// 2. 调用 Shiro 的权限验证器
}
3. 权限验证流程
切面类调用 Shiro 的 AuthorizingAnnotationMethodInterceptor,执行以下步骤:
解析注解:提取注解中的权限字符串(如 “user:delete”)。
获取当前用户:通过 SecurityUtils.getSubject() 获取当前 Subject(用户主体)。
权限校验:
调用 subject.checkPermission(“user:delete”)。
若用户无权限,抛出 UnauthorizedException 并终止方法执行。
放行执行:校验通过后,执行原方法逻辑。
🔧 二、关键技术支持
- 动态代理机制
Spring 集成:通过 DefaultAdvisorAutoProxyCreator 为 Bean 创建代理对象,使注解生效。
配置示例:
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator">
<property name="proxyTargetClass" value="true"/> <!-- 强制使用 CGLIB 代理 -->
</bean>
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor"/>
- Shiro 的权限解析器
PermissionResolver 将字符串权限(如 “user:delete”)转换为可比较的权限对象。
支持通配符匹配(如 “user:*” 匹配所有用户操作)。 - 与 Realm 的协作
权限校验最终委托给 Realm 的 doGetAuthorizationInfo() 方法,从数据库或缓存加载用户权限数据。
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.addStringPermission("user:delete"); // 从数据库查询实际权限
return info;
}

四、注意事项
注解作用位置:
建议用于 Controller 层(Service 层若使用 Spring 事务注解,可能导致代理冲突)。
动态代理限制:
类内部方法调用(如 this.deleteUser())会绕过代理,导致注解失效。
权限粒度:
注解控制方法级别权限,比 URL 拦截更细粒度(如限制 deleteUser() 但允许 getUser())。
异常处理:
权限不足时抛出 AuthorizationException,需全局异常处理器统一处理。
💎 总结
Shiro 的注解权限控制本质是 AOP 动态代理 + 权限验证框架 的协作结果:
注解声明:标记方法所需权限。
代理拦截:AOP 切面在方法执行前触发校验。
权限验证:通过 Realm 加载数据,由 SecurityManager 比对权限。
执行控制:校验失败阻止方法执行,成功则放行。
火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。
更多推荐
所有评论(0)