Shiro性能优化与安全加固实战指南之二
Shiro常见问题与优化实战指南
性能优化
缓存策略选择
Shiro的权限验证过程可能频繁访问数据库,合理的缓存策略能显著提升性能。
权限缓存时间设置:
// 在自定义Realm中配置缓存
public class MyRealm extends AuthorizingRealm {
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
// 获取授权信息后会自动缓存
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.addRole("admin");
info.addStringPermission("user:delete");
return info;
}
// 配置缓存名称和过期时间
@PostConstruct
public void initCredentialsMatcher() {
setAuthorizationCacheName("authorizationCache");
setAuthorizationCachingEnabled(true);
}
}
实践建议:
- 生产环境建议缓存时间设置为30分钟-2小时
- 敏感权限建议缩短缓存时间或实时验证
- 使用Redis等分布式缓存时注意序列化配置
减少Realm重复查询
// 实现CacheableAuthenticationInfo接口
public class MyRealm extends AuthorizingRealm
implements CacheableAuthenticationInfo {
@Override
public boolean isAuthenticationCachingEnabled(Object principal,
AuthenticationToken token) {
// 只缓存成功的认证
return token instanceof UsernamePasswordToken;
}
@Override
public Object getAuthenticationCacheKey(PrincipalCollection principals) {
// 使用用户名作为缓存键
return principals.getPrimaryPrincipal();
}
}
安全加固
防止暴力破解
登录失败次数限制实现:
public class RetryLimitCredentialsMatcher extends HashedCredentialsMatcher {
private Cache<String, AtomicInteger> passwordRetryCache;
public RetryLimitCredentialsMatcher(CacheManager cacheManager) {
passwordRetryCache = cacheManager.getCache("passwordRetryCache");
}
@Override
public boolean doCredentialsMatch(AuthenticationToken token,
AuthenticationInfo info) {
String username = (String) token.getPrincipal();
AtomicInteger retryCount = passwordRetryCache.get(username);
if (retryCount == null) {
retryCount = new AtomicInteger(0);
passwordRetryCache.put(username, retryCount);
}
if (retryCount.incrementAndGet() > 5) {
throw new ExcessiveAttemptsException();
}
boolean matches = super.doCredentialsMatch(token, info);
if (matches) {
passwordRetryCache.remove(username);
}
return matches;
}
}
敏感操作二次验证
// 在控制器中添加验证
@RequiresPermissions("account:transfer")
public void transferFunds(BigDecimal amount, String password) {
Subject subject = SecurityUtils.getSubject();
if (!subject.isAuthenticated()) {
throw new UnauthorizedException();
}
// 二次密码验证
UsernamePasswordToken token = new UsernamePasswordToken(
subject.getPrincipal().toString(),
password
);
try {
subject.login(token);
} catch (AuthenticationException e) {
throw new VerificationFailedException("二次验证失败");
}
// 执行转账逻辑
accountService.transfer(amount);
}
调试技巧
Shiro日志配置
在logback.xml
中添加:
<logger name="org.apache.shiro" level="DEBUG"/>
<logger name="org.apache.shiro.web.filter" level="TRACE"/>
典型调试场景输出:
DEBUG org.apache.shiro.authc.AbstractAuthenticator - Authentication successful
TRACE org.apache.shiro.web.filter.PathMatchingFilter - Matching path [/admin] with pattern [/admin/**]
获取用户上下文
// 在任何地方获取当前用户
Subject currentUser = SecurityUtils.getSubject();
// 检查角色
if (currentUser.hasRole("admin")) {
// 管理员操作
}
// 检查权限
if (currentUser.isPermitted("user:create")) {
// 创建用户
}
// 获取会话
Session session = currentUser.getSession();
session.setAttribute("key", "value");
实践建议:
- 避免频繁调用
SecurityUtils.getSubject()
,可缓存结果 - Web环境中注意线程安全问题
- 异步任务中需要手动绑定Subject
总结优化方案
通过以上优化措施,可以使Shiro在保证安全性的同时获得更好的性能表现。建议在实际项目中根据具体场景选择合适的优化组合。
评论已关闭