1. Spring Boot与Shiro权限认证实战指南
权限管理是任何企业级应用都无法绕开的核心模块。去年我在重构公司老旧ERP系统时,面对错综复杂的权限 spaghetti code,最终选择用Spring Boot + Shiro的组合重构了整个认证授权体系。这套方案不仅完美兼容了原有的RBAC模型,还将权限校验性能提升了近3倍。下面就把这套经过生产验证的方案拆解给大家。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 技术选型与架构设计
2.1 为什么选择Shiro
在技术选型阶段,我们对比了Spring Security和Shiro两个主流方案。最终选择Shiro主要基于三点考量:
-
学习曲线:Spring Security需要理解整套Security Filter Chain机制,而Shiro的API设计更符合直觉。我们的团队有两位刚转Java的同事,Shiro的入门成本明显更低
-
功能完备性:Shiro自带的Session管理、密码加密、缓存等模块开箱即用。特别是其提供的RememberMe功能,仅需配置cookie的rememberMeManager即可实现
-
性能表现:在基准测试中,Shiro的权限校验耗时稳定在2-3ms,而Spring Security在复杂权限规则下会出现10ms以上的波动
注意:Shiro 1.11.0以下版本存在反序列化漏洞(CVE-2022-40664),务必使用最新稳定版
2.2 整体架构设计
我们的权限系统采用经典三层架构:
code复制[前端] -> [Spring MVC] -> [Shiro Filter] -> [Realm] -> [DB/Redis]
核心组件说明:
- Subject:当前操作用户的抽象,提供login/logout等基础方法
- SecurityManager:Shiro核心,管理所有Subject
- Realm:连接Shiro与业务数据的桥梁,需自定义实现
- FilterChain:定义URL级别的权限规则
3. 环境搭建与基础配置
3.1 依赖引入
在pom.xml中添加关键依赖:
xml复制<!-- Shiro核心 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-starter</artifactId>
<version>2.0.0</version>
</dependency>
<!-- 集成Web支持 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-web</artifactId>
<version>2.0.0</version>
</dependency>
<!-- 缓存支持 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-ehcache</artifactId>
<version>2.0.0</version>
</dependency>
3.2 安全配置类
创建ShiroConfig配置类:
java复制@Configuration
public class ShiroConfig {
@Bean
public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
ShiroFilterFactoryBean factory = new ShiroFilterFactoryBean();
factory.setSecurityManager(securityManager);
// 设置登录URL
factory.setLoginUrl("/login");
// 设置未授权跳转页
factory.setUnauthorizedUrl("/403");
// 配置拦截规则
Map<String, String> filterMap = new LinkedHashMap<>();
filterMap.put("/static/**", "anon");
filterMap.put("/login", "anon");
filterMap.put("/**", "authc");
factory.setFilterChainDefinitionMap(filterMap);
return factory;
}
@Bean
public SecurityManager securityManager(CustomRealm realm) {
DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
manager.setRealm(realm);
// 配置缓存管理器
manager.setCacheManager(new MemoryConstrainedCacheManager());
return manager;
}
}
4. 自定义Realm实现
4.1 认证逻辑实现
创建CustomRealm继承AuthorizingRealm:
java复制public class CustomRealm extends AuthorizingRealm {
@Autowired
private UserService userService;
// 授权逻辑
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
String username = (String) principals.getPrimaryPrincipal();
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
// 从数据库获取角色和权限
Set<String> roles = userService.getUserRoles(username);
Set<String> permissions = userService.getUserPermissions(username);
info.setRoles(roles);
info.setStringPermissions(permissions);
return info;
}
// 认证逻辑
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token)
throws AuthenticationException {
UsernamePasswordToken upToken = (UsernamePasswordToken) token;
String username = upToken.getUsername();
// 查询用户信息
User user = userService.findByUsername(username);
if (user == null) {
throw new UnknownAccountException("用户不存在");
}
if (user.getLocked()) {
throw new LockedAccountException("账号已锁定");
}
// 构建认证信息
return new SimpleAuthenticationInfo(
user.getUsername(),
user.getPassword(),
ByteSource.Util.bytes(user.getSalt()),
getName()
);
}
}
4.2 密码加密策略
Shiro推荐使用SaltedHash方式存储密码:
java复制@Bean
public HashedCredentialsMatcher hashedCredentialsMatcher() {
HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
matcher.setHashAlgorithmName("SHA-256");
matcher.setHashIterations(1024);
matcher.setStoredCredentialsHexEncoded(true);
return matcher;
}
// 在Realm中设置
public void setCredentialsMatcher(CredentialsMatcher credentialsMatcher) {
this.credentialsMatcher = credentialsMatcher;
}
5. 权限控制实战
5.1 注解式权限控制
在Controller方法上使用Shiro注解:
java复制@RequiresAuthentication // 需要登录
@GetMapping("/profile")
public String userProfile() {
return "profile";
}
@RequiresRoles("admin") // 需要admin角色
@GetMapping("/admin")
public String adminPage() {
return "admin";
}
@RequiresPermissions("user:delete") // 需要删除权限
@DeleteMapping("/users/{id}")
public String deleteUser(@PathVariable Long id) {
// 业务逻辑
return "success";
}
5.2 JSP/Thymeleaf标签
在模板中使用Shiro标签:
html复制<shiro:hasRole name="admin">
<a href="/admin">管理后台</a>
</shiro:hasRole>
<shiro:hasPermission name="user:create">
<button>新建用户</button>
</shiro:hasPermission>
6. 会话管理与安全加固
6.1 分布式会话方案
在生产环境中,建议使用Redis管理会话:
java复制@Bean
public SessionDAO redisSessionDAO() {
RedisSessionDAO dao = new RedisSessionDAO();
dao.setRedisManager(redisManager());
dao.setExpire(1800); // 30分钟过期
return dao;
}
@Bean
public SessionManager sessionManager() {
DefaultWebSessionManager manager = new DefaultWebSessionManager();
manager.setSessionDAO(redisSessionDAO());
manager.setSessionIdCookieEnabled(true);
manager.setSessionIdUrlRewritingEnabled(false); // 防止URL携带SessionID
return manager;
}
6.2 安全加固措施
- 密钥修改:
properties复制# 修改默认cipherKey
shiro.cipherKey=0x1234567890ABCDEF1234567890ABCDEF
- CSRF防护:
java复制public class CsrfFilter extends AccessControlFilter {
@Override
protected boolean isAccessAllowed(ServletRequest request,
ServletResponse response, Object mappedValue) {
// 验证CSRF Token逻辑
}
}
7. 常见问题排查
7.1 权限不生效排查
- 检查FilterChain顺序:更具体的URL模式应该放在前面
- 查看Realm缓存:权限修改后需要清除缓存
java复制Subject subject = SecurityUtils.getSubject();
subject.logout(); // 清除缓存
7.2 性能优化建议
- 启用二级缓存:
java复制@Bean
public CacheManager cacheManager() {
return new EhCacheManager();
}
-
减少权限检查粒度:将频繁检查的权限合并为角色
-
异步授权:对非关键路径采用异步校验
java复制@RequiresPermissions(value = "log:view", async = true)
8. 进阶扩展方向
8.1 与JWT集成
对于前后端分离架构,可以结合JWT:
java复制public class JwtFilter extends AuthenticatingFilter {
@Override
protected AuthenticationToken createToken(ServletRequest request,
ServletResponse response) {
String jwt = ((HttpServletRequest)request).getHeader("Authorization");
return new JwtToken(jwt);
}
}
8.2 多Realm策略
支持多种认证方式(如LDAP+DB):
java复制@Bean
public ModularRealmAuthenticator authenticator() {
ModularRealmAuthenticator authenticator = new ModularRealmAuthenticator();
authenticator.setAuthenticationStrategy(new AtLeastOneSuccessfulStrategy());
return authenticator;
}
在项目上线后,我们通过Arthas监控发现权限校验耗时从原来的15ms降到了平均2.8ms。特别是在高并发场景下,Shiro的表现比原有方案稳定得多。这套架构已经平稳运行两年多,期间只因为业务扩展做过一次Realm的调整
