1. SpringBoot3与Shiro集成概述
在Java企业级应用开发中,安全框架的选择直接影响系统的防护能力。Apache Shiro作为轻量级安全框架,相比Spring Security具有更简单的API和更低的认知成本。随着SpringBoot3的正式发布,基于Java17的特性支持为Shiro集成带来了新的可能性。
我最近在一个电商后台管理系统的项目中采用了SpringBoot3+Shiro的技术组合,实测发现这种搭配既能享受SpringBoot3的现代化特性,又能快速实现RBAC权限控制。下面将完整分享这套方案的实现细节和避坑指南。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 环境准备与基础配置
2.1 依赖管理配置
首先需要在pom.xml中添加关键依赖(Gradle用户请对应调整):
xml复制<dependencies>
<!-- SpringBoot3基础依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>3.0.0</version>
</dependency>
<!-- Shiro核心库 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-starter</artifactId>
<version>1.11.0</version>
</dependency>
<!-- 数据持久化支持 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-ehcache</artifactId>
<version>1.11.0</version>
</dependency>
</dependencies>
注意:Shiro官方starter目前最新版是1.11.0,虽然版本号不高但完全兼容SpringBoot3。不要被版本号迷惑,实际测试中运行非常稳定。
2.2 基础配置类实现
创建Shiro核心配置类ShiroConfig.java:
java复制@Configuration
public class ShiroConfig {
@Bean
public Realm customRealm() {
CustomRealm realm = new CustomRealm();
realm.setCredentialsMatcher(hashedCredentialsMatcher());
return realm;
}
@Bean
public HashedCredentialsMatcher hashedCredentialsMatcher() {
HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
matcher.setHashAlgorithmName("SHA-256");
matcher.setHashIterations(1024);
matcher.setStoredCredentialsHexEncoded(false);
return matcher;
}
@Bean
public DefaultWebSecurityManager securityManager() {
DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
manager.setRealm(customRealm());
manager.setCacheManager(ehCacheManager());
return manager;
}
@Bean
public EhCacheManager ehCacheManager() {
EhCacheManager cacheManager = new EhCacheManager();
cacheManager.setCacheManagerConfigFile("classpath:ehcache-shiro.xml");
return cacheManager;
}
}
3. 自定义Realm实现
3.1 核心认证逻辑
创建自定义Realm类继承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()
);
}
}
3.2 密码加密策略
在用户注册/密码修改时需要使用相同的加密策略:
java复制public class PasswordHelper {
private static final String ALGORITHM = "SHA-256";
private static final int ITERATIONS = 1024;
public static String encryptPassword(String password, String salt) {
SimpleHash hash = new SimpleHash(
ALGORITHM,
password,
ByteSource.Util.bytes(salt),
ITERATIONS
);
return hash.toHex();
}
public static String generateSalt() {
SecureRandom random = new SecureRandom();
byte[] salt = new byte[16];
random.nextBytes(salt);
return Hex.encodeHexString(salt);
}
}
4. 权限控制实现
4.1 注解式权限控制
在Controller方法上使用Shiro注解:
java复制@RestController
@RequestMapping("/admin")
public class AdminController {
@RequiresRoles("admin")
@GetMapping("/users")
public List<User> listUsers() {
return userService.listAllUsers();
}
@RequiresPermissions("user:delete")
@DeleteMapping("/user/{id}")
public void deleteUser(@PathVariable Long id) {
userService.deleteUser(id);
}
}
4.2 URL级别权限配置
在ShiroConfig中添加过滤器链:
java复制@Bean
public ShiroFilterFactoryBean shiroFilter(DefaultWebSecurityManager securityManager) {
ShiroFilterFactoryBean factory = new ShiroFilterFactoryBean();
factory.setSecurityManager(securityManager);
Map<String, String> filterMap = new LinkedHashMap<>();
// 静态资源放行
filterMap.put("/static/**", "anon");
// 登录接口放行
filterMap.put("/auth/login", "anon");
// 其他请求需要认证
filterMap.put("/**", "authc");
factory.setFilterChainDefinitionMap(filterMap);
return factory;
}
5. 会话管理与缓存
5.1 Ehcache配置
创建ehcache-shiro.xml配置文件:
xml复制<ehcache updateCheck="false" monitoring="autodetect">
<diskStore path="java.io.tmpdir/shiro-spring-sample"/>
<cache name="shiro-activeSessionCache"
maxEntriesLocalHeap="10000"
timeToLiveSeconds="0"
timeToIdleSeconds="0"
eternal="true"
overflowToDisk="true">
</cache>
<cache name="authorizationCache"
maxEntriesLocalHeap="1000"
timeToLiveSeconds="3600"
timeToIdleSeconds="1800"
overflowToDisk="false">
</cache>
</ehcache>
5.2 会话超时配置
在application.properties中设置:
properties复制# Session超时时间(毫秒)
shiro.session.globalSessionTimeout=1800000
# 删除无效session
shiro.session.deleteInvalidSessions=true
# 定时检查session
shiro.session.validationSchedulerEnabled=true
6. 常见问题与解决方案
6.1 注解不生效问题
如果发现@RequiresRoles等注解不生效,检查以下配置:
- 确保在启动类上添加
@EnableAspectJAutoProxy - 检查ShiroConfig中是否配置了以下Bean:
java复制@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(
DefaultWebSecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
advisor.setSecurityManager(securityManager);
return advisor;
}
6.2 跨域问题处理
当整合前后端分离项目时,需要在Shiro过滤器中放行OPTIONS请求:
java复制filterMap.put("/**", "authc");
// 调整为
filterMap.put("/**", "cors,authc");
// 并添加自定义CORS过滤器
@Bean
public FilterRegistrationBean<CorsFilter> corsFilter() {
FilterRegistrationBean<CorsFilter> registration = new FilterRegistrationBean<>();
registration.setFilter(new CorsFilter());
registration.setOrder(Ordered.HIGHEST_PRECEDENCE);
return registration;
}
6.3 性能优化建议
- 权限缓存:确保启用authorizationCache,避免频繁查询数据库
- Session持久化:生产环境建议使用Redis等分布式缓存替代默认的Ehcache
- 细粒度控制:对于高频接口,可以使用
@RequiresPermissions替代URL级别的过滤
7. 进阶功能实现
7.1 分布式Session共享
在微服务架构下,需要实现Session共享:
java复制@Bean
public SessionDAO redisSessionDAO() {
RedisSessionDAO sessionDAO = new RedisSessionDAO();
sessionDAO.setRedisManager(redisManager());
sessionDAO.setSessionIdGenerator(new JavaUuidSessionIdGenerator());
sessionDAO.setExpire(1800);
return sessionDAO;
}
@Bean
public DefaultWebSessionManager sessionManager() {
DefaultWebSessionManager manager = new DefaultWebSessionManager();
manager.setSessionDAO(redisSessionDAO());
return manager;
}
7.2 多Realm支持
对于复杂系统可能需要多个Realm:
java复制@Bean
public ModularRealmAuthenticator authenticator() {
ModularRealmAuthenticator authenticator = new ModularRealmAuthenticator();
authenticator.setAuthenticationStrategy(new AtLeastOneSuccessfulStrategy());
return authenticator;
}
@Bean
public DefaultWebSecurityManager securityManager() {
DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
manager.setAuthenticator(authenticator());
manager.setRealms(Arrays.asList(jdbcRealm(), ldapRealm()));
return manager;
}
在实际项目中,SpringBoot3与Shiro的集成方案已经过多个生产环境验证。关键点在于合理配置缓存策略和会话管理,避免性能瓶颈。对于新项目,建议直接采用本文的密码加密方案,避免后期安全升级带来的兼容性问题。
