SpringBoot實(shí)現(xiàn)賬號(hào)登錄錯(cuò)誤次數(shù)的限制和鎖定功能
Pre
SpringBoot - 優(yōu)雅的實(shí)現(xiàn)【流控】
需求
需求描述:
- 登錄錯(cuò)誤次數(shù)限制:在用戶登錄時(shí),記錄每個(gè)賬號(hào)的登錄錯(cuò)誤次數(shù),并限制連續(xù)錯(cuò)誤的次數(shù)。
- 賬號(hào)鎖定機(jī)制:當(dāng)一個(gè)賬號(hào)連續(xù)輸入錯(cuò)誤密碼超過5次時(shí),該賬號(hào)將被鎖定15分鐘。在15分鐘后,賬號(hào)會(huì)自動(dòng)解鎖。
- 自動(dòng)解鎖功能:賬號(hào)在連續(xù)錯(cuò)誤輸入超過5次后,將觸發(fā)鎖定機(jī)制,并且5分鐘后自動(dòng)解鎖,利用Redis的鍵值存儲(chǔ)來管理錯(cuò)誤次數(shù)和鎖定時(shí)間。
- 配置文件:登錄錯(cuò)誤次數(shù)的限制(如5次錯(cuò)誤)和賬號(hào)鎖定時(shí)間(如15分鐘)應(yīng)該能通過配置文件進(jìn)行設(shè)置,以便靈活配置。
- 自定義注解實(shí)現(xiàn):使用自定義注解來實(shí)現(xiàn)登錄錯(cuò)誤次數(shù)限制與賬號(hào)鎖定功能的邏輯。
技術(shù)細(xì)節(jié):
- 使用Redis的Key來存儲(chǔ)和管理每個(gè)用戶的錯(cuò)誤登錄次數(shù)和鎖定狀態(tài)。
- 自定義注解實(shí)現(xiàn)錯(cuò)誤次數(shù)和鎖定時(shí)長的判斷與控制。
- 錯(cuò)誤次數(shù)和鎖定時(shí)長通過配置文件(如
application.yml
或application.properties
)進(jìn)行配置,支持靈活調(diào)整。
實(shí)現(xiàn)步驟
簡易實(shí)現(xiàn)
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中配置相關(guān)參數(shù):
3. 自定義注解
創(chuàng)建一個(gè)自定義注解@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 { // 默認(rèn)值依賴配置文件,可在此處設(shè)定默認(rèn)值 int maxAttempts() default 5; int lockTime() default 15; }
4. AOP切面
創(chuàng)建一個(gè)AOP切面來處理登錄錯(cuò)誤次數(shù)的限制和鎖定邏輯:
@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 = ""; // 根據(jù)登錄類型獲取對(duì)應(yīng)的鍵 // # 0 賬號(hào)密碼模式 1 key模式(默認(rèn)) 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(); } // 檢查賬號(hào)是否已被鎖定 if (loginAttemptValidator.isLocked(lockKey)) { throw new ServiceException(TOO_MANY_REQUESTS.getCode(), "賬號(hào)被鎖定,請稍后重試"); } // 獲取登錄次數(shù) int attempts = loginAttemptValidator.getAttempt(attemptKey); // 檢查登錄嘗試次數(shù)是否超過最大限制 if (attempts >= maxAttempts) { loginAttemptValidator.setLock(lockKey, lockTime); loginAttemptValidator.resetAttempt(attemptKey); throw new ServiceException(TOO_MANY_REQUESTS.getCode(), "賬號(hào)被鎖定,請稍后重試"); } try { // 執(zhí)行登錄操作 Object result = joinPoint.proceed(); // 登錄成功,重置登錄嘗試計(jì)數(shù) loginAttemptValidator.resetAttempt(attemptKey); return result; } catch (Exception e) { // 登錄失敗,增加登錄嘗試計(jì)數(shù) loginAttemptValidator.incrementAttempt(attemptKey); throw e; } } }
5. 使用自定義注解:
在服務(wù)的方法上添加自定義注解
6. 測試
創(chuàng)建一個(gè)控制器來處理登錄請求
連續(xù)錯(cuò)誤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; /** * 嘗試次數(shù)自增 * * @param key Redis中的鍵,用于標(biāo)識(shí)特定的嘗試計(jì)數(shù) */ public void incrementAttempt(String key) { redisTemplate.opsForValue().increment(key); } /** * 獲取嘗試次數(shù) * 如果給定鍵的嘗試次數(shù)在緩存中不存在,則初始化嘗試次數(shù)為0 * 此方法主要用于跟蹤某些操作的嘗試次數(shù),例如登錄嘗試次數(shù),以防止暴力破解 * * @param key 緩存中的鍵,通常與特定用戶或操作相關(guān)聯(lián) * @return 嘗試次數(shù)如果緩存中沒有對(duì)應(yīng)的嘗試記錄,則返回0 */ public int getAttempt(String key) { // 從Redis中獲取嘗試次數(shù) Integer attempts = redisTemplate.opsForValue().get(key); // 如果嘗試次數(shù)為空,則初始化嘗試次數(shù) if (attempts == null ) initAttempt(key); // 返回嘗試次數(shù)如果為null,則返回0 return attempts == null ? 0 : attempts; } /** * 初始化嘗試次數(shù) * 該方法用于在Redis中初始化一個(gè)鍵的嘗試次數(shù)為0 * 主要用于記錄和管理操作的嘗試次數(shù),以便進(jìn)行后續(xù)的限制或監(jiān)控 * * @param key Redis中的鍵,用于唯一標(biāo)識(shí)一個(gè)操作或請求 */ public void initAttempt(String key) { redisTemplate.opsForValue().set(key, 0); } /** * 重置嘗試次數(shù) * 通過刪除Redis中的鍵來重置特定嘗試的計(jì)數(shù) * * @param key Redis中用于標(biāo)識(shí)嘗試計(jì)數(shù)的鍵 */ public void resetAttempt(String key) { redisTemplate.delete(key); } /** * 設(shè)置緩存鎖 * * @param key 鎖的唯一標(biāo)識(shí),通常使用業(yè)務(wù)鍵作為鎖的key * @param duration 鎖的持有時(shí)間,單位為分鐘 * * 此方法旨在通過Redis實(shí)現(xiàn)分布式鎖的功能,通過設(shè)置一個(gè)具有過期時(shí)間的鍵值對(duì)來實(shí)現(xiàn) * 鍵值對(duì)的key為業(yè)務(wù)鍵,值為鎖定標(biāo)志,過期時(shí)間由參數(shù)duration指定 */ public void setLock(String key, long duration) { redisTemplate.opsForValue().set(key, RedisKeyConstants.LOCK_FLAG, duration, TimeUnit.MINUTES); } /** * 檢查給定鍵是否處于鎖定狀態(tài) * * @param key 要檢查的鍵 * @return 如果鍵未鎖定,則返回false;如果鍵已鎖定,則返回true */ public boolean isLocked(String key) { // 從Redis中獲取鍵對(duì)應(yīng)的值 Integer value = redisTemplate.opsForValue().get(key); // 如果值為空,則表明鍵未鎖定,返回false if (ObjectUtil.isEmpty(value)) return false ; // 比較鍵的值是否與鎖定標(biāo)志相等,如果相等則表明鍵已鎖定,返回true return RedisKeyConstants.LOCK_FLAG == redisTemplate.opsForValue().get(key); } }
總結(jié)
基于Spring Boot的賬號(hào)登錄錯(cuò)誤次數(shù)限制和鎖定功能,使用了Redis來存儲(chǔ)登錄失敗次數(shù)和鎖定狀態(tài),并通過自定義注解和AOP來實(shí)現(xiàn)切面邏輯。配置文件中可以靈活配置最大嘗試次數(shù)和鎖定時(shí)長。
到此這篇關(guān)于SpringBoot實(shí)現(xiàn)賬號(hào)登錄錯(cuò)誤次數(shù)的限制和鎖定功能的文章就介紹到這了,更多相關(guān)SpringBoot賬號(hào)登錄錯(cuò)誤鎖定內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Spring Boot中擴(kuò)展XML請求與響應(yīng)的支持詳解
這篇文章主要給大家介紹了關(guān)于Spring Boot中擴(kuò)展XML請求與響應(yīng)的支持的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2018-09-09Spring5+SpringMvc+Hibernate5整合的實(shí)現(xiàn)
這篇文章主要介紹了Spring5+SpringMvc+Hibernate5整合的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-06-06Java靜態(tài)static與實(shí)例instance方法示例
這篇文章主要為大家介紹了Java靜態(tài)static與實(shí)例instance方法示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-08-08springsecurity基于token的認(rèn)證方式
本文主要介紹了springsecurity基于token的認(rèn)證方式,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-08-08