Shiro集成与配置实战:从基础到Spring Boot整合
Shiro常规用法详解:从集成到实战配置
一、集成方式
1. 独立应用集成(非Spring环境)
在纯Java项目中集成Shiro的核心步骤:
// 1. 创建SecurityManager并配置Realm
DefaultSecurityManager securityManager = new DefaultSecurityManager();
securityManager.setRealm(new MyCustomRealm());
// 2. 设置全局SecurityManager
SecurityUtils.setSecurityManager(securityManager);
// 3. 获取当前Subject
Subject currentUser = SecurityUtils.getSubject();
// 4. 执行认证
UsernamePasswordToken token = new UsernamePasswordToken("username", "password");
try {
currentUser.login(token); // 认证成功
} catch (AuthenticationException ae) {
// 认证失败处理
}
实践建议:适合非Web应用或遗留系统改造,但建议优先考虑Spring集成方案
2. 与Spring/Spring Boot整合
Spring Boot中的典型配置:
@Bean
public ShiroFilterFactoryBean shiroFilter(DefaultWebSecurityManager securityManager) {
ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
factoryBean.setSecurityManager(securityManager);
// 配置拦截规则
Map<String, String> filterChainMap = new LinkedHashMap<>();
filterChainMap.put("/static/**", "anon");
filterChainMap.put("/login", "anon");
filterChainMap.put("/**", "authc");
factoryBean.setFilterChainDefinitionMap(filterChainMap);
return factoryBean;
}
@Bean
public DefaultWebSecurityManager securityManager(MyRealm realm) {
DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
manager.setRealm(realm);
manager.setCacheManager(new RedisCacheManager()); // 使用Redis缓存
return manager;
}
关键点:
ShiroFilterFactoryBean
是Web集成的核心入口- 拦截规则按顺序匹配,
anon
表示匿名访问,authc
需要认证 - 通过
DefaultWebSecurityManager
管理安全组件
二、配置示例
1. INI配置方式(传统方式)
[main]
# 定义Realm
myRealm = com.example.MyRealm
securityManager.realms = $myRealm
# 加密配置
credentialsMatcher = org.apache.shiro.authc.credential.HashedCredentialsMatcher
credentialsMatcher.hashAlgorithmName = SHA-256
credentialsMatcher.hashIterations = 1024
myRealm.credentialsMatcher = $credentialsMatcher
[urls]
/login = anon
/admin/** = roles[admin]
/user/** = perms["user:manage"]
适用场景:简单应用或快速原型开发,但灵活性不如Java配置
2. 注解驱动安全控制
@Controller
public class AdminController {
@RequiresRoles("admin")
@GetMapping("/admin/dashboard")
public String adminDashboard() {
return "admin/dashboard";
}
@RequiresPermissions("user:delete")
@DeleteMapping("/user/{id}")
public ResponseEntity deleteUser(@PathVariable Long id) {
// 删除用户逻辑
}
}
注意:需要在Spring中启用注解支持:
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(
DefaultWebSecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
advisor.setSecurityManager(securityManager);
return advisor;
}
三、常见场景实现
1. 登录流程定制
自定义FormAuthenticationFilter示例:
public class CustomFormAuthFilter extends FormAuthenticationFilter {
@Override
protected boolean onLoginSuccess(AuthenticationToken token,
Subject subject, ServletRequest request, ServletResponse response) {
// 登录成功后记录日志或初始化用户会话
return super.onLoginSuccess(token, subject, request, response);
}
@Override
protected boolean onLoginFailure(AuthenticationToken token,
AuthenticationException e, ServletRequest request, ServletResponse response) {
// 登录失败处理(如记录失败次数)
return super.onLoginFailure(token, e, request, response);
}
}
配置自定义Filter:
@Bean
public ShiroFilterFactoryBean shiroFilter(...) {
...
Map<String, Filter> filters = new HashMap<>();
filters.put("authc", new CustomFormAuthFilter());
factoryBean.setFilters(filters);
...
}
2. 动态URL权限控制
结合数据库实现动态规则:
public class DynamicFilterChainManager {
public void loadFilterChains(ShiroFilterFactoryBean factoryBean) {
// 从数据库读取权限规则
List<PermissionRule> rules = permissionRuleDao.findAll();
Map<String, String> chains = new LinkedHashMap<>();
rules.forEach(rule -> {
chains.put(rule.getUrl(), rule.getDefinition());
});
factoryBean.setFilterChainDefinitionMap(chains);
}
}
最佳实践:建议配合缓存使用,避免每次请求都查询数据库
3. 自定义Realm实现
public class MyRealm extends AuthorizingRealm {
@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("用户不存在");
}
// 返回认证信息(包含凭证和盐值)
return new SimpleAuthenticationInfo(
user.getUsername(),
user.getPassword(),
ByteSource.Util.bytes(user.getSalt()),
getName()
);
}
@Override
protected AuthorizationInfo doGetAuthorizationInfo(
PrincipalCollection principals) {
String username = (String) principals.getPrimaryPrincipal();
User user = userService.findByUsername(username);
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
// 添加角色
info.setRoles(user.getRoles());
// 添加权限
info.setStringPermissions(user.getPermissions());
return info;
}
}
关键点:
- 认证方法
doGetAuthenticationInfo
负责验证用户身份 - 授权方法
doGetAuthorizationInfo
负责加载用户权限 - 建议将密码比较逻辑交给Shiro处理(通过CredentialsMatcher)
四、扩展点实践
1. 自定义JWT Token支持
public class JWTToken implements AuthenticationToken {
private String token;
public JWTToken(String token) {
this.token = token;
}
@Override
public Object getPrincipal() {
return JWTUtil.parseUsername(token);
}
@Override
public Object getCredentials() {
return token;
}
}
// 配套的Realm
public class JWTRealm extends AuthorizingRealm {
@Override
public boolean supports(AuthenticationToken token) {
return token instanceof JWTToken;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(...) {
String jwt = (String) token.getCredentials();
// 验证JWT签名、有效期等
if (!JWTUtil.verify(jwt)) {
throw new AuthenticationException("Invalid token");
}
return new SimpleAuthenticationInfo(username, jwt, getName());
}
// ... 授权方法同上
}
2. 集成Redis缓存
public class RedisCacheManager implements CacheManager {
@Override
public <K, V> Cache<K, V> getCache(String name) {
return new RedisCache<>(name, redisTemplate);
}
}
public class RedisCache<K, V> implements Cache<K, V> {
private final String cacheKey;
private final RedisTemplate<K, V> redisTemplate;
public RedisCache(String name, RedisTemplate<K, V> redisTemplate) {
this.cacheKey = "shiro:cache:" + name;
this.redisTemplate = redisTemplate;
}
@Override
public V get(K key) {
return redisTemplate.opsForHash().get(cacheKey, key);
}
@Override
public V put(K key, V value) {
redisTemplate.opsForHash().put(cacheKey, key, value);
return value;
}
// 实现其他必要方法...
}
性能建议:
- 为认证和授权信息设置不同的过期时间
- 对高频访问的权限数据启用缓存
- 实现缓存清理机制(如权限变更时)
五、总结图表
通过本文介绍的各种配置方式和实践示例,开发者可以快速掌握Shiro在项目中的常规用法。建议根据实际项目需求选择合适的集成方案,并重点关注认证授权流程的安全性和性能优化。
评论已关闭