概述
前10分钟内累计3次验证失败后,增加图形验证码验证条件,前10分钟内累计6次验证失败后,系统自动锁定该账号15分钟,15分钟后自动解锁;
方案
基于redisson(zset)滑动时间窗记录最近10分钟内该账户登录失败次数
- 统计次数、每次失败加1
java
/**
* 统计请求次数
*
* @param windowTime 窗口时间,单位:秒
* @param key 登录账户
* @return 请求次数
*/
public Integer count(int windowTime, String key) {
// 窗口结束时间
long windowEndTime = System.currentTimeMillis();
// 窗口开始时间
long windowStartTime = windowEndTime - windowTime * 1000L;
try {
// 创建 RBatch 实例,批量执行命令
RBatch batch = redissonClient.createBatch();
// 添加元素 score=当前时间戳 value=请求序列号,唯一不可重复
batch.getScoredSortedSet(key).addAsync(windowEndTime, UUID.randomUUID().toString());
// 统计数据
batch.getScoredSortedSet(key).countAsync(windowStartTime, true, windowEndTime, true);
// 清除窗口过期成员
batch.getScoredSortedSet(key).removeRangeByScoreAsync(0, true, windowStartTime, false);
// 设置key过期时间
batch.getScoredSortedSet(key).expireAsync(Duration.ofSeconds(windowTime));
// 执行管道命令
BatchResult<?> batchResult = batch.execute();
// 返回统计数量
List<?> responses = batchResult.getResponses();
Integer number = (Integer) responses.get(1);
return number;
} catch (Exception e) {
log.error("统计请求次数异常!", e);
return null;
}
}
- 获取登录失败次数
java
/**
* 获取对应窗口的次数
* @param windowTime 窗口大小十分钟,600s
* @param key
* @return
*/
public Integer getLastCachedCount(int windowTime, String key) {
// 窗口结束时间
long windowEndTime = System.currentTimeMillis();
// 窗口开始时间
long windowStartTime = windowEndTime - windowTime * 1000L;
try {
// 创建 RBatch 实例,批量执行命令
RBatch batch = redissonClient.createBatch();
// 统计数据,获取上一次缓存存取的数据
RFuture<Integer> countFuture = batch.getScoredSortedSet(key).countAsync(windowStartTime, true, windowEndTime, true);
// 执行管道命令
batch.execute();
// 等待统计数据完成并获取结果
Integer count = countFuture.get();
// 如果结果为null,则返回0,否则返回计数值
return count == null ? 0 : count.intValue();
} catch (Exception e) {
log.error("获取上一次缓存存取数据异常!", e);
// 如果出现异常,返回null
return null;
}
}
- 清除登录失败次数
java
public void clearCachedCount(int windowTime, String key) {
// 窗口结束时间
long windowEndTime = System.currentTimeMillis();
// 窗口开始时间
long windowStartTime = windowEndTime - windowTime * 1000L;
try {
// 创建 RBatch 实例,批量执行命令
RBatch batch = redissonClient.createBatch();
// 统计数据,获取上一次缓存存取的数据
batch.getScoredSortedSet(key).removeRangeByScoreAsync(windowStartTime, true, windowEndTime, true);
// 执行管道命令
batch.execute();
// 如果结果为null,则返回0,否则返回计数值
} catch (Exception e) {
log.error("清楚登录失败记录次数异常", e);
}
}