1. 为什么选择SpringBoot3与Shiro的组合
在Java生态中,权限管理一直是系统开发的核心需求。Apache Shiro作为轻量级安全框架,以其简洁的API和全面的安全功能著称。而SpringBoot3作为最新一代的快速开发框架,带来了对Java17、GraalVM原生镜像等新特性的支持。将二者结合,既能享受SpringBoot的便捷,又能获得Shiro强大的权限控制能力。
我最近在一个物联网后台管理系统中实际采用了这个组合。项目需要精细的权限控制(设备操作权限、数据查看权限等),同时要求快速迭代。SpringBoot3的自动配置与Shiro的灵活性完美匹配,从零搭建到完整权限系统只用了3天。下面分享具体实现过程中积累的一手经验。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 环境准备与基础配置
2.1 依赖管理关键点
使用SpringBoot3时需特别注意版本兼容性。以下是必须的依赖项(Gradle示例):
groovy复制dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web:3.1.0'
implementation 'org.apache.shiro:shiro-spring-boot-web-starter:2.0.0'
implementation 'org.apache.shiro:shiro-core:2.0.0'
// 数据库相关
implementation 'com.baomidou:mybatis-plus-boot-starter:3.5.3.1'
implementation 'mysql:mysql-connector-java:8.0.33'
}
重要提示:Shiro 2.x版本才完全兼容SpringBoot3,1.x版本会出现自动配置失效问题。我在初期就踩过这个坑,导致花了半天时间排查为什么过滤器不生效。
2.2 最小化配置示例
在application.yml中必须配置的基本项:
yaml复制shiro:
enabled: true
web:
enabled: true
loginUrl: /login
unauthorizedUrl: /403
这里有个实际项目中的经验:unauthorizedUrl一定要配置,否则当用户访问未授权页面时,Shiro会抛出异常而不是跳转到友好提示页。我们在测试阶段就因此收到过用户体验投诉。
3. 核心组件实现详解
3.1 Realm的定制化开发
Realm是Shiro与业务系统对接的桥梁。以下是结合MyBatis-Plus的数据库鉴权实现:
java复制public class AdminRealm extends AuthorizingRealm {
@Autowired
private UserMapper userMapper;
@Autowired
private RoleService roleService;
// 授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
String username = (String) principals.getPrimaryPrincipal();
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
// 获取用户角色
Set<String> roles = roleService.getRolesByUsername(username);
info.setRoles(roles);
// 获取用户权限
Set<String> permissions = roleService.getPermissionsByUsername(username);
info.setStringPermissions(permissions);
return info;
}
// 认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token)
throws AuthenticationException {
UsernamePasswordToken upToken = (UsernamePasswordToken) token;
String username = upToken.getUsername();
// 数据库查询
User user = userMapper.selectOne(new LambdaQueryWrapper<User>()
.eq(User::getUsername, username));
if (user == null) {
throw new UnknownAccountException("用户不存在");
}
return new SimpleAuthenticationInfo(
username,
user.getPassword(),
ByteSource.Util.bytes(user.getSalt()),
getName()
);
}
}
关键经验:
- 认证信息中一定要包含salt,否则密码比对会失败
- 授权信息建议做缓存,否则每次权限检查都会查询数据库
- 实际项目中我们发现角色和权限的查询SQL很频繁,后来用Redis做了二级缓存
3.2 Shiro过滤链配置
URL级别的权限控制在ShiroFilterFactoryBean中定义:
java复制@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(
DefaultWebSecurityManager securityManager) {
ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
factoryBean.setSecurityManager(securityManager);
// 动态获取过滤链
Map<String, String> filterMap = new LinkedHashMap<>();
filterMap.put("/login", "anon");
filterMap.put("/static/**", "anon");
filterMap.put("/logout", "logout");
filterMap.put("/**", "authc");
factoryBean.setFilterChainDefinitionMap(filterMap);
return factoryBean;
}
实际项目中我们把这个配置改造成了从数据库读取,实现了动态权限配置。这里有个坑:过滤链的顺序非常重要,匹配规则是从上到下执行,所以通配符/**必须放在最后。
4. 高级功能与性能优化
4.1 会话集群方案
在分布式环境下,需要解决Session共享问题。我们采用Redis存储方案:
java复制@Bean
public SessionDAO sessionDAO() {
RedisSessionDAO sessionDAO = new RedisSessionDAO();
sessionDAO.setRedisManager(redisManager());
sessionDAO.setExpire(1800); // 30分钟过期
return sessionDAO;
}
@Bean
public SessionsSecurityManager securityManager(Realm realm) {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(realm);
securityManager.setSessionManager(sessionManager());
return securityManager;
}
private SessionManager sessionManager() {
DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
sessionManager.setSessionDAO(sessionDAO());
return sessionManager;
}
性能提示:Session序列化方式对性能影响很大。我们测试发现,使用Kryo序列化比默认的JDK序列化快3倍以上,但要注意Kryo的线程安全问题。
4.2 接口级权限控制
除了URL级别的控制,我们还需要方法级别的细粒度权限:
java复制@RequiresPermissions("user:delete")
@DeleteMapping("/users/{id}")
public Result deleteUser(@PathVariable Long id) {
// 业务逻辑
}
@RequiresRoles("admin")
@GetMapping("/system/config")
public Result getSystemConfig() {
// 业务逻辑
}
在Controller方法上添加注解即可实现方法级权限控制。但要注意:这些注解是通过AOP实现的,所以调用同类中的方法时会失效。我们曾经因此出现过权限绕过漏洞,后来通过规范代码结构解决了这个问题。
5. 常见问题排查指南
5.1 过滤器不生效问题
现象:配置的Shiro过滤器没有拦截请求。
排查步骤:
- 检查依赖版本是否兼容(SpringBoot3必须用Shiro2.x)
- 确认
@Configuration类被正确扫描 - 检查过滤链顺序,确保通配规则在后
- 查看SpringBoot自动配置报告(访问
/actuator/env)
5.2 权限缓存不一致
现象:修改用户权限后,旧权限仍然有效。
解决方案:
- 实现CacheManager接口
- 在修改权限时手动清除缓存:
java复制Cache<Object, AuthorizationInfo> cache = authorizationCache();
cache.remove(username);
我们在生产环境遇到过缓存不一致导致的管理员权限丢失问题,后来增加了权限变更时的缓存清除逻辑,并添加了缓存监控。
6. 安全加固建议
6.1 防范会话固定攻击
在ShiroConfig中添加:
java复制@Bean
public DefaultSubjectFactory subjectFactory() {
DefaultWebSubjectFactory factory = new DefaultWebSubjectFactory();
factory.setSessionCreationEnabled(false);
return factory;
}
6.2 密码加密策略
推荐使用PBKDF2WithHmacSHA256算法:
java复制@Bean
public HashedCredentialsMatcher hashedCredentialsMatcher() {
HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
matcher.setHashAlgorithmName("PBKDF2WithHmacSHA256");
matcher.setHashIterations(10000);
matcher.setStoredCredentialsHexEncoded(false);
return matcher;
}
在实际压力测试中,这个算法比MD5慢约100倍,但安全性大幅提升。建议根据业务需求调整迭代次数。
7. 与SpringSecurity的对比选型
在技术选型时,我们对比了Shiro和SpringSecurity:
| 特性 | Shiro | SpringSecurity |
|---|---|---|
| 学习曲线 | 平缓 | 陡峭 |
| 配置复杂度 | 简单 | 复杂 |
| 功能完整性 | 基础安全功能 | 企业级安全功能 |
| 微服务支持 | 需要扩展 | 原生支持 |
| 性能开销 | 低 | 中等 |
| 社区活跃度 | 活跃 | 非常活跃 |
最终选择Shiro的原因:
- 项目不需要OAuth2等复杂协议
- 团队有Shiro使用经验
- 系统对性能要求较高
这套组合在日活50万的系统中运行稳定,平均鉴权耗时在3ms以内。后期我们可能会对会话管理部分进行优化,考虑引入JWT方案。
