SpringBoot实现账号登录错误次数的限制和锁定功能
作者:小小工匠
本文介绍了如何使用SpringBoot和Redis实现账号登录错误次数限制和锁定功能,通过自定义注解和AOP切面,结合配置文件灵活设置最大尝试次数和锁定时长,感兴趣的朋友跟随小编一起看看吧
Pre
需求
需求描述:
- 登录错误次数限制:在用户登录时,记录每个账号的登录错误次数,并限制连续错误的次数。
- 账号锁定机制:当一个账号连续输入错误密码超过5次时,该账号将被锁定15分钟。在15分钟后,账号会自动解锁。
- 自动解锁功能:账号在连续错误输入超过5次后,将触发锁定机制,并且5分钟后自动解锁,利用Redis的键值存储来管理错误次数和锁定时间。
- 配置文件:登录错误次数的限制(如5次错误)和账号锁定时间(如15分钟)应该能通过配置文件进行设置,以便灵活配置。
- 自定义注解实现:使用自定义注解来实现登录错误次数限制与账号锁定功能的逻辑。
技术细节:
- 使用Redis的Key来存储和管理每个用户的错误登录次数和锁定状态。
- 自定义注解实现错误次数和锁定时长的判断与控制。
- 错误次数和锁定时长通过配置文件(如
application.yml
或application.properties
)进行配置,支持灵活调整。
实现步骤
简易实现
1. 添加依赖
首先,在pom.xml中添加必要的依赖:
<dependencies> <!-- Spring Boot Starter Data Redis --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <!-- Spring Boot Starter Web --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- Spring Boot Starter AOP --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> </dependencies>
2. 配置文件
在application.yml中配置相关参数:
3. 自定义注解
创建一个自定义注解@LoginAttemptLimit:
import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface LoginAttemptLimit { // 默认值依赖配置文件,可在此处设定默认值 int maxAttempts() default 5; int lockTime() default 15; }
4. AOP切面
创建一个AOP切面来处理登录错误次数的限制和锁定逻辑:
@Aspect @Slf4j @Component public class LoginAttemptLimitAspect { @Resource private LoginAttemptValidator loginAttemptValidator; @Resource private AdminAuthService authService; @Value("${supervision.max-attempts:2}") private int maxAttempts; @Value("${supervision.lock-time:5}") private long lockTime; @Around("@annotation(loginAttemptLimit)") public Object limitLoginAttempts(ProceedingJoinPoint joinPoint, LoginAttemptLimit loginAttemptLimit) throws Throwable { String attemptKey = ""; String lockKey = ""; // 根据登录类型获取对应的键 // # 0 账号密码模式 1 key模式(默认) if (authService.getLoginType() == 1) { AuthKeyLoginReqVO authKeyLoginReqVO = (AuthKeyLoginReqVO) joinPoint.getArgs()[0]; attemptKey = RedisKeyConstants.ATTEMP_KEY_PREFIX + authKeyLoginReqVO.getUsername(); lockKey = RedisKeyConstants.LOCK_KEY_PREFIX + authKeyLoginReqVO.getUsername(); } else { AuthLoginReqVO authLoginReqVO = (AuthLoginReqVO) joinPoint.getArgs()[0]; attemptKey = RedisKeyConstants.ATTEMP_KEY_PREFIX + authLoginReqVO.getUsername(); lockKey = RedisKeyConstants.LOCK_KEY_PREFIX + authLoginReqVO.getUsername(); } // 检查账号是否已被锁定 if (loginAttemptValidator.isLocked(lockKey)) { throw new ServiceException(TOO_MANY_REQUESTS.getCode(), "账号被锁定,请稍后重试"); } // 获取登录次数 int attempts = loginAttemptValidator.getAttempt(attemptKey); // 检查登录尝试次数是否超过最大限制 if (attempts >= maxAttempts) { loginAttemptValidator.setLock(lockKey, lockTime); loginAttemptValidator.resetAttempt(attemptKey); throw new ServiceException(TOO_MANY_REQUESTS.getCode(), "账号被锁定,请稍后重试"); } try { // 执行登录操作 Object result = joinPoint.proceed(); // 登录成功,重置登录尝试计数 loginAttemptValidator.resetAttempt(attemptKey); return result; } catch (Exception e) { // 登录失败,增加登录尝试计数 loginAttemptValidator.incrementAttempt(attemptKey); throw e; } } }
5. 使用自定义注解:
在服务的方法上添加自定义注解
6. 测试
创建一个控制器来处理登录请求
连续错误5次后,
Redis中Key的TTL
附
import cn.hutool.core.util.ObjectUtil; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Component; import javax.annotation.Resource; import java.util.concurrent.TimeUnit; @Component public class LoginAttemptValidator { @Resource private RedisTemplate<String,Integer> redisTemplate; /** * 尝试次数自增 * * @param key Redis中的键,用于标识特定的尝试计数 */ public void incrementAttempt(String key) { redisTemplate.opsForValue().increment(key); } /** * 获取尝试次数 * 如果给定键的尝试次数在缓存中不存在,则初始化尝试次数为0 * 此方法主要用于跟踪某些操作的尝试次数,例如登录尝试次数,以防止暴力破解 * * @param key 缓存中的键,通常与特定用户或操作相关联 * @return 尝试次数如果缓存中没有对应的尝试记录,则返回0 */ public int getAttempt(String key) { // 从Redis中获取尝试次数 Integer attempts = redisTemplate.opsForValue().get(key); // 如果尝试次数为空,则初始化尝试次数 if (attempts == null ) initAttempt(key); // 返回尝试次数如果为null,则返回0 return attempts == null ? 0 : attempts; } /** * 初始化尝试次数 * 该方法用于在Redis中初始化一个键的尝试次数为0 * 主要用于记录和管理操作的尝试次数,以便进行后续的限制或监控 * * @param key Redis中的键,用于唯一标识一个操作或请求 */ public void initAttempt(String key) { redisTemplate.opsForValue().set(key, 0); } /** * 重置尝试次数 * 通过删除Redis中的键来重置特定尝试的计数 * * @param key Redis中用于标识尝试计数的键 */ public void resetAttempt(String key) { redisTemplate.delete(key); } /** * 设置缓存锁 * * @param key 锁的唯一标识,通常使用业务键作为锁的key * @param duration 锁的持有时间,单位为分钟 * * 此方法旨在通过Redis实现分布式锁的功能,通过设置一个具有过期时间的键值对来实现 * 键值对的key为业务键,值为锁定标志,过期时间由参数duration指定 */ public void setLock(String key, long duration) { redisTemplate.opsForValue().set(key, RedisKeyConstants.LOCK_FLAG, duration, TimeUnit.MINUTES); } /** * 检查给定键是否处于锁定状态 * * @param key 要检查的键 * @return 如果键未锁定,则返回false;如果键已锁定,则返回true */ public boolean isLocked(String key) { // 从Redis中获取键对应的值 Integer value = redisTemplate.opsForValue().get(key); // 如果值为空,则表明键未锁定,返回false if (ObjectUtil.isEmpty(value)) return false ; // 比较键的值是否与锁定标志相等,如果相等则表明键已锁定,返回true return RedisKeyConstants.LOCK_FLAG == redisTemplate.opsForValue().get(key); } }
总结
基于Spring Boot的账号登录错误次数限制和锁定功能,使用了Redis来存储登录失败次数和锁定状态,并通过自定义注解和AOP来实现切面逻辑。配置文件中可以灵活配置最大尝试次数和锁定时长。
到此这篇关于SpringBoot实现账号登录错误次数的限制和锁定功能的文章就介绍到这了,更多相关SpringBoot账号登录错误锁定内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!