Commit 20ca8a31 by weisong
parents 525f5bb3 c9be99c8
package com.cnooc.expert.auth.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.util.concurrent.TimeUnit;
@Service
public class AccountLockService {
@Autowired
private StringRedisTemplate redisTemplate;
// 锁定策略:3次错误 -> 10分钟,4次错误 -> 30分钟,5次及以上错误 -> 1小时
private static final int[] LOCK_DURATIONS = {10, 30, 60}; // 分钟
private static final int MAX_ATTEMPTS = 3;
private String getLockKey(String account) {
return "login:lock:" + account;
}
private String getAttemptKey(String account) {
return "login:attempt:" + account;
}
private int calculateLockMinutes(long failureCount) {
if (failureCount < MAX_ATTEMPTS) {
return 0; // 不锁定
} else if (failureCount == MAX_ATTEMPTS) {
return LOCK_DURATIONS[0]; // 10分钟
} else if (failureCount == MAX_ATTEMPTS + 1) {
return LOCK_DURATIONS[1]; // 30分钟
} else {
return LOCK_DURATIONS[2]; // 60分钟
}
}
private void lockAccount(String account, int lockMinutes) {
String lockKey = getLockKey(account);
String lockValue = LocalDateTime.now().toString() + "|" + lockMinutes;
redisTemplate.opsForValue().set(lockKey, lockValue, lockMinutes, TimeUnit.MINUTES);//这个超时时间需要考虑一下
}
/**
* 手动解锁账号
*/
public void unlockAccount(String account) {
String attemptKey = getAttemptKey(account);
String lockKey = getLockKey(account);
redisTemplate.delete(attemptKey);
redisTemplate.delete(lockKey);
}
/**
* 处理登录成功(重置失败计数和锁定)
*/
public void handleLoginSuccess(String account) {
String attemptKey = getAttemptKey(account);
String lockKey = getLockKey(account);
redisTemplate.delete(attemptKey);
redisTemplate.delete(lockKey);
}
/**
* 处理登录失败
*/
public void handleLoginFailure(String account) {
String attemptKey = getAttemptKey(account);
String lockKey = getLockKey(account);
// 如果已经锁定,不再增加计数
if (Boolean.TRUE.equals(redisTemplate.hasKey(lockKey))) {
return;
}
// 增加失败计数
//long failureCount = jedis.incr(attemptKey);
String failureCountStr = redisTemplate.opsForValue().get(attemptKey)!=null?redisTemplate.opsForValue().get(attemptKey).toString():"0";
long failureCount = Long.parseLong(failureCountStr)+1;
redisTemplate.opsForValue().set(attemptKey,String.valueOf(failureCount));
// 设置计数过期时间(避免无限增长)
if (failureCount == 1) {
redisTemplate.expire(attemptKey, 24 * 60, TimeUnit.MINUTES); // 24小时过期
}
// 根据失败次数确定锁定时间
int lockMinutes = calculateLockMinutes(failureCount);
if (lockMinutes > 0) {
lockAccount(account, lockMinutes);
// 重置失败计数(锁定期间不需要继续计数)
redisTemplate.delete(attemptKey);
}
}
/**
* 检查账号是否被锁定
*/
public boolean isAccountLocked(String account) {
String lockKey = getLockKey(account);
return Boolean.TRUE.equals(redisTemplate.hasKey(lockKey));
}
/**
* 获取锁定剩余时间
*/
public long getLockRemainingTime(String account) {
String lockKey = getLockKey(account);
boolean isExist = Boolean.TRUE.equals(redisTemplate.hasKey(lockKey));
if(isExist){
return redisTemplate.getExpire(lockKey,TimeUnit.MINUTES);
}else{
return 0;
}
}
}
......@@ -26,6 +26,7 @@ public interface GlobalErrorCodeConstants {
ErrorCode PASSWORD_ERROR = new ErrorCode(2003, "密码错误");
ErrorCode USER_DISABLED = new ErrorCode(2004, "用户已被禁用");
ErrorCode PASSWORD_NOT_EXIST = new ErrorCode(2005, "密码不存在,请先重置密码");
ErrorCode USER_LOCKED = new ErrorCode(2006, "账号已锁定");
// ========== 参数校验错误 (3000-3999) ==========
ErrorCode PARAM_REQUIRED = new ErrorCode(3001, "必填字段不能为空");
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment