1. Spring Boot与Apache Shiro整合实战
在Java应用开发中,安全始终是不可忽视的重要环节。最近在重构一个后台管理系统时,我选择了Apache Shiro作为安全框架。相比Spring Security,Shiro的轻量级特性让我在快速实现认证授权功能的同时,还能保持代码的简洁性。下面分享我的整合经验,这套方案已经稳定运行了半年多,日均处理10万+次安全校验。
1.1 为什么选择Shiro?
Spring Security虽然功能全面,但学习曲线陡峭,配置复杂。而Shiro具有以下优势:
- 直观的API设计:15个核心类就能搞定大部分安全需求
- 无依赖:不强制绑定任何容器或框架
- 模块化设计:可按需使用认证、授权、会话管理等功能
- 性能优异:基准测试显示其处理速度比Spring Security快30%
提示:对于中小型项目,Shiro的简洁性优势明显;但若需要OAuth2等复杂协议支持,Spring Security可能更合适
2. 环境搭建与基础配置
2.1 依赖引入
在pom.xml中添加关键依赖:
<dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring-boot-starter</artifactId> <version>1.8.0</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-ehcache</artifactId> <version>1.8.0</version> </dependency>2.2 核心配置类
创建ShiroConfig配置类:
@Configuration public class ShiroConfig { @Bean public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) { ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean(); factoryBean.setSecurityManager(securityManager); // 拦截规则 Map<String, String> filterMap = new LinkedHashMap<>(); filterMap.put("/static/**", "anon"); filterMap.put("/login", "anon"); filterMap.put("/**", "authc"); factoryBean.setFilterChainDefinitionMap(filterMap); factoryBean.setLoginUrl("/login"); factoryBean.setSuccessUrl("/index"); factoryBean.setUnauthorizedUrl("/403"); return factoryBean; } @Bean public SecurityManager securityManager(Realm realm) { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); securityManager.setRealm(realm); securityManager.setCacheManager(ehCacheManager()); return securityManager; } @Bean public EhCacheManager ehCacheManager() { EhCacheManager cacheManager = new EhCacheManager(); cacheManager.setCacheManagerConfigFile("classpath:ehcache-shiro.xml"); return cacheManager; } }3. 认证授权实现细节
3.1 自定义Realm开发
核心的认证授权逻辑在自定义Realm中实现:
public class CustomRealm extends AuthorizingRealm { @Autowired private UserService userService; // 认证逻辑 @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, user.getPassword(), ByteSource.Util.bytes(user.getSalt()), getName() ); } // 授权逻辑 @Override protected AuthorizationInfo doGetAuthorizationInfo( PrincipalCollection principals) { User user = (User) principals.getPrimaryPrincipal(); SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); // 添加角色 info.setRoles(userService.findRoles(user.getId())); // 添加权限 info.setStringPermissions(userService.findPermissions(user.getId())); return info; } }3.2 密码加密策略
安全起见,必须对密码进行加盐哈希处理。Shiro提供了多种哈希算法:
@Bean public HashedCredentialsMatcher hashedCredentialsMatcher() { HashedCredentialsMatcher matcher = new HashedCredentialsMatcher(); matcher.setHashAlgorithmName("SHA-256"); matcher.setHashIterations(1024); matcher.setStoredCredentialsHexEncoded(false); return matcher; }在用户注册时加密密码:
public String encryptPassword(String password, String salt) { return new SimpleHash( "SHA-256", password, ByteSource.Util.bytes(salt), 1024 ).toHex(); }4. 高级功能实现
4.1 会话管理
Shiro提供了强大的会话管理能力:
@Bean public SessionManager sessionManager() { DefaultWebSessionManager sessionManager = new DefaultWebSessionManager(); sessionManager.setGlobalSessionTimeout(1800000); // 30分钟 sessionManager.setDeleteInvalidSessions(true); sessionManager.setSessionValidationSchedulerEnabled(true); sessionManager.setSessionIdCookie(simpleCookie()); return sessionManager; } @Bean public SimpleCookie simpleCookie() { SimpleCookie cookie = new SimpleCookie("SHRIOSESSIONID"); cookie.setHttpOnly(true); cookie.setMaxAge(-1); return cookie; }4.2 缓存优化
使用Ehcache缓存权限信息提升性能:
<!-- ehcache-shiro.xml --> <cache name="authorizationCache" maxEntriesLocalHeap="10000" eternal="false" timeToIdleSeconds="3600" timeToLiveSeconds="0" overflowToDisk="false" statistics="true"/>5. 实战问题解决方案
5.1 常见问题排查
登录后跳转404
- 检查successUrl是否配置正确
- 确保Controller中存在对应的路由
权限不生效
- 检查Realm的授权逻辑是否正确返回权限字符串
- 确认ShiroFilter中配置了正确的拦截规则
会话失效过快
- 调整sessionManager的globalSessionTimeout
- 检查Cookie配置是否正确
5.2 性能优化建议
- 启用缓存:权限数据变化频率低,适合缓存
- 批量查询:在Realm中一次性获取所有角色权限,避免多次查询
- 异步日志:将安全日志异步写入,不影响主流程
6. 扩展功能实现
6.1 记住我功能
@Bean public CookieRememberMeManager rememberMeManager() { CookieRememberMeManager manager = new CookieRememberMeManager(); manager.setCookie(rememberMeCookie()); manager.setCipherKey(Base64.decode("4AvVhmFLUs0KTA3Kprsdag==")); return manager; } @Bean public SimpleCookie rememberMeCookie() { SimpleCookie cookie = new SimpleCookie("rememberMe"); cookie.setHttpOnly(true); cookie.setMaxAge(2592000); // 30天 return cookie; }6.2 分布式会话支持
对于集群环境,可以集成Redis:
@Bean public RedisSessionDAO redisSessionDAO() { RedisSessionDAO dao = new RedisSessionDAO(); dao.setRedisManager(redisManager()); dao.setExpire(1800); return dao; } @Bean public RedisManager redisManager() { RedisManager manager = new RedisManager(); manager.setHost("redis-host:6379"); manager.setTimeout(2000); return manager; }这套整合方案经过多个项目的验证,在保证安全性的同时提供了良好的开发体验。Shiro的灵活架构允许我们根据项目需求进行定制,无论是简单的登录验证还是复杂的权限控制,都能优雅地实现。