1. SpringBoot3与Shiro集成概述
在Java企业级应用开发中,安全框架的选择直接影响系统的防护能力。Apache Shiro作为轻量级安全框架,以其简洁的API和全面的安全功能受到开发者青睐。而SpringBoot3作为当前最新的SpringBoot大版本,带来了诸多新特性。将两者结合,既能享受SpringBoot的便捷,又能获得Shiro强大的安全控制。
我最近在一个电商后台管理系统中实践了SpringBoot3与Shiro的集成方案,发现相比传统Spring Security,Shiro的配置更加直观,学习曲线更平缓。特别是对于需要快速上线的项目,Shiro的"开箱即用"特性可以节省大量开发时间。
2. 环境准备与基础配置
2.1 依赖引入
首先需要在pom.xml中添加必要的依赖:
<dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring-boot-starter</artifactId> <version>1.11.0</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>注意:SpringBoot3要求Java17+环境,而Shiro1.11.0版本已完全兼容SpringBoot3。如果使用旧版Shiro可能会出现兼容性问题。
2.2 基础配置类
创建Shiro的核心配置类:
@Configuration public class ShiroConfig { @Bean public ShiroFilterFactoryBean shiroFilterFactoryBean( DefaultWebSecurityManager securityManager) { ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean(); factoryBean.setSecurityManager(securityManager); // 设置登录URL factoryBean.setLoginUrl("/login"); // 设置未授权跳转URL factoryBean.setUnauthorizedUrl("/403"); // 配置拦截规则 Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>(); filterChainDefinitionMap.put("/static/**", "anon"); filterChainDefinitionMap.put("/login", "anon"); filterChainDefinitionMap.put("/**", "authc"); factoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); return factoryBean; } @Bean public DefaultWebSecurityManager securityManager(Realm realm) { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); securityManager.setRealm(realm); return securityManager; } }3. 认证与授权实现
3.1 自定义Realm开发
Realm是Shiro的核心组件,负责实际的认证和授权逻辑:
public class CustomRealm extends AuthorizingRealm { @Autowired private UserService userService; // 授权逻辑 @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { String username = (String) principals.getPrimaryPrincipal(); SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo(); // 从数据库获取角色和权限 Set<String> roles = userService.findRoles(username); Set<String> permissions = userService.findPermissions(username); authorizationInfo.setRoles(roles); authorizationInfo.setStringPermissions(permissions); return authorizationInfo; } // 认证逻辑 @Override protected AuthenticationInfo doGetAuthenticationInfo( AuthenticationToken token) throws AuthenticationException { String username = (String) token.getPrincipal(); 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()); } }3.2 密码加密配置
Shiro推荐使用加密存储密码,避免明文存储的安全风险:
@Bean public HashedCredentialsMatcher hashedCredentialsMatcher() { HashedCredentialsMatcher matcher = new HashedCredentialsMatcher(); matcher.setHashAlgorithmName("SHA-256"); matcher.setHashIterations(1024); matcher.setStoredCredentialsHexEncoded(false); return matcher; } @Bean public Realm realm() { CustomRealm realm = new CustomRealm(); realm.setCredentialsMatcher(hashedCredentialsMatcher()); return realm; }4. 高级功能实现
4.1 会话管理
Shiro提供了完善的会话管理功能:
@Bean public SessionManager sessionManager() { DefaultWebSessionManager sessionManager = new DefaultWebSessionManager(); // 设置会话超时时间(毫秒) sessionManager.setGlobalSessionTimeout(1800000); // 删除无效session sessionManager.setDeleteInvalidSessions(true); // 定时检查session sessionManager.setSessionValidationSchedulerEnabled(true); return sessionManager; } // 在securityManager中注入 @Bean public DefaultWebSecurityManager securityManager(Realm realm, SessionManager sessionManager) { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); securityManager.setRealm(realm); securityManager.setSessionManager(sessionManager); return securityManager; }4.2 记住我功能
实现"记住我"功能可以提升用户体验:
@Bean public CookieRememberMeManager rememberMeManager() { CookieRememberMeManager rememberMeManager = new CookieRememberMeManager(); rememberMeManager.setCipherKey(Base64.decode("yourBase64EncodedKeyHere")); SimpleCookie cookie = new SimpleCookie("rememberMe"); cookie.setHttpOnly(true); cookie.setMaxAge(2592000); // 30天 rememberMeManager.setCookie(cookie); return rememberMeManager; } // 在ShiroFilterFactoryBean中添加filter filterChainDefinitionMap.put("/home", "user"); filterChainDefinitionMap.put("/**", "authc");5. 安全防护与最佳实践
5.1 防止Shiro反序列化漏洞
Shiro历史上存在反序列化漏洞,必须采取防护措施:
@Bean public RememberMeManager rememberMeManager() { CookieRememberMeManager manager = new CookieRememberMeManager(); // 必须设置自定义密钥,不要使用默认密钥 manager.setCipherKey("yourCustomKeyHere".getBytes(StandardCharsets.UTF_8)); return manager; }5.2 性能优化建议
- 缓存配置:Shiro的授权信息应该缓存
@Bean public CacheManager cacheManager() { return new MemoryConstrainedCacheManager(); // 生产环境建议使用Redis等分布式缓存 } @Bean public Realm realm() { CustomRealm realm = new CustomRealm(); realm.setCacheManager(cacheManager()); realm.setCachingEnabled(true); realm.setAuthenticationCachingEnabled(true); realm.setAuthenticationCacheName("authenticationCache"); realm.setAuthorizationCachingEnabled(true); realm.setAuthorizationCacheName("authorizationCache"); return realm; }- 减少授权检查次数:对于频繁访问的接口,可以使用注解缓存权限验证结果
@RequiresPermissions("user:view") @GetMapping("/users") public List<User> listUsers() { // ... }6. 常见问题排查
6.1 登录失败无错误提示
问题现象:登录失败但页面没有显示错误信息。
解决方案:
- 检查Shiro配置中是否设置了
loginUrl - 确保登录页面有错误显示逻辑:
<span th:if="${shiroLoginFailure != null}" th:text="${shiroLoginFailure}"></span>6.2 权限注解不生效
问题现象:在Controller方法上添加@RequiresRoles等注解无效。
解决方案:
- 确保在Spring配置中启用了Shiro注解:
@Bean public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor( DefaultWebSecurityManager securityManager) { AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor(); advisor.setSecurityManager(securityManager); return advisor; }- 确保在启动类上添加
@EnableAspectJAutoProxy
6.3 会话失效问题
问题现象:用户会话意外失效。
排查步骤:
- 检查session超时时间配置
- 检查服务器是否重启导致内存会话丢失
- 生产环境建议配置持久化会话存储
7. 实际项目中的经验分享
在最近的项目中,我们遇到了几个值得分享的问题:
- 动态权限控制:对于需要动态变更权限的系统,我们实现了数据库驱动的权限管理:
@Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { // 每次授权都从数据库获取最新权限 refreshPermissionsFromDB(); // ... }- 分布式会话:在微服务架构下,我们整合了Shiro与Spring Session:
@Bean public SessionManager sessionManager(RedisOperationsSessionRepository sessionRepository) { SpringSessionManager sessionManager = new SpringSessionManager(); sessionManager.setSessionRepository(sessionRepository); return sessionManager; }- JWT集成:对于前后端分离项目,我们扩展了Shiro支持JWT认证:
public class JWTFilter extends AuthenticatingFilter { // 实现JWT解析和认证逻辑 }提示:在SpringBoot3中,WebFlux逐渐成为主流,但Shiro目前主要支持Servlet环境。如果需要响应式编程支持,可以考虑Spring Security或等待Shiro的响应式版本。