feat: 添加用户角色字段并实现权限控制
fix(security): 修复重放攻击拦截器的时间戳验证漏洞 refactor(security): 重构验证码工具类使用线程安全实现 perf(login): 优化登录锁定工具类性能并添加定期清理 fix(editor): 修复笔记编辑器空指针问题 style: 清理数据库索引脚本中的冗余注释 fix(api): 修复前端API调用参数编码问题 feat(image): 实现图片名称同步服务 refactor(markdown): 重构Markdown服务分离图片名称同步逻辑 fix(xss): 添加HTML转义函数防止XSS攻击 fix(user): 修复用户服务权限加载问题 fix(rate-limit): 修复速率限制拦截器并发问题 fix(axios): 生产环境隐藏详细错误信息 fix(image): 修复图片上传和删除的权限验证 refactor(captcha): 重构验证码工具类使用并发安全实现 fix(jwt): 修复JWT过滤器空指针问题 fix(export): 修复笔记导出XSS漏洞 fix(search): 修复Markdown搜索SQL注入问题 fix(interceptor): 修复重放攻击拦截器逻辑错误 fix(controller): 修复用户控制器空指针问题 fix(security): 修复nonce生成使用密码学安全方法
This commit is contained in:
@@ -6,11 +6,14 @@ import java.time.LocalDateTime;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
|
||||
/**
|
||||
* 登录锁定工具类
|
||||
* 使用本地内存存储登录失败记录,带容量限制
|
||||
* 使用本地内存存储登录失败记录,带容量限制和定期清理
|
||||
*/
|
||||
@Slf4j
|
||||
public class LoginLockUtil {
|
||||
@@ -28,6 +31,19 @@ public class LoginLockUtil {
|
||||
private static final LRUCache<String, LoginAttempt> attempts = new LRUCache<>(MAX_RECORDS);
|
||||
private static final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
|
||||
|
||||
// 定期清理线程池
|
||||
private static final ScheduledExecutorService cleanupScheduler = Executors.newSingleThreadScheduledExecutor(r -> {
|
||||
Thread t = new Thread(r, "login-lock-cleanup");
|
||||
t.setDaemon(true);
|
||||
return t;
|
||||
});
|
||||
|
||||
static {
|
||||
// 每10分钟清理一次过期记录
|
||||
cleanupScheduler.scheduleAtFixedRate(LoginLockUtil::cleanupExpiredLocks,
|
||||
10, 10, TimeUnit.MINUTES);
|
||||
}
|
||||
|
||||
private static class LoginAttempt {
|
||||
int failedCount;
|
||||
LocalDateTime lastAttemptTime;
|
||||
@@ -38,6 +54,10 @@ public class LoginLockUtil {
|
||||
this.lastAttemptTime = LocalDateTime.now();
|
||||
this.lockUntil = null;
|
||||
}
|
||||
|
||||
boolean isExpired() {
|
||||
return ChronoUnit.MINUTES.between(lastAttemptTime, LocalDateTime.now()) > RECORD_EXPIRE_MINUTES;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -68,8 +88,6 @@ public class LoginLockUtil {
|
||||
public static void recordFailedAttempt(String username) {
|
||||
if (username == null || username.isEmpty()) return;
|
||||
|
||||
cleanupExpiredRecords();
|
||||
|
||||
lock.writeLock().lock();
|
||||
try {
|
||||
LoginAttempt attempt = attempts.computeIfAbsent(username, k -> new LoginAttempt());
|
||||
@@ -116,20 +134,7 @@ public class LoginLockUtil {
|
||||
|
||||
// 检查是否仍在锁定时间内
|
||||
if (attempt.lockUntil != null) {
|
||||
if (LocalDateTime.now().isBefore(attempt.lockUntil)) {
|
||||
return true;
|
||||
} else {
|
||||
// 锁定时间已过,清除记录
|
||||
lock.readLock().unlock();
|
||||
lock.writeLock().lock();
|
||||
try {
|
||||
attempts.remove(username);
|
||||
} finally {
|
||||
lock.writeLock().unlock();
|
||||
lock.readLock().lock();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return LocalDateTime.now().isBefore(attempt.lockUntil);
|
||||
}
|
||||
return false;
|
||||
} finally {
|
||||
@@ -177,14 +182,9 @@ public class LoginLockUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理过期记录
|
||||
* 清理过期的锁定记录
|
||||
*/
|
||||
private static void cleanupExpiredRecords() {
|
||||
// 每100次操作清理一次,减少开销
|
||||
if (attempts.size() < MAX_RECORDS * 0.8) {
|
||||
return;
|
||||
}
|
||||
|
||||
private static void cleanupExpiredLocks() {
|
||||
lock.writeLock().lock();
|
||||
try {
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
@@ -192,7 +192,7 @@ public class LoginLockUtil {
|
||||
LoginAttempt attempt = entry.getValue();
|
||||
// 未锁定且长时间没有登录的记录
|
||||
if (attempt.lockUntil == null) {
|
||||
return ChronoUnit.MINUTES.between(attempt.lastAttemptTime, now) > RECORD_EXPIRE_MINUTES;
|
||||
return attempt.isExpired();
|
||||
}
|
||||
// 锁定已过期的记录
|
||||
return now.isAfter(attempt.lockUntil);
|
||||
|
||||
Reference in New Issue
Block a user