防刷發送短信驗證碼接口的五種簡單好用方法,絕對夠用
-
前端增加圖形驗證碼,點擊發送按鈕后增加60s倒計時,60s后才可以再次點擊
-
后端對接口次數校驗,60s內同一電話號碼只能發送一次
// 生成基于電話號碼的重試鎖定鍵 String repeatLock = StringUtils.join("send_sms:", mobile);// 使用 Redis 的 setIfAbsent 進行原子性操作,嘗試設置鍵值對,如果鍵已存在則返回 false if (!redisTemplate.opsForValue().setIfAbsent(repeatLock, "locked", 60, TimeUnit.SECONDS)) {// 鍵已存在,表示1分鐘內已經發送過短信LOGGER.error("60s內不能重復調用發送短信功能: {}", mobile);throw new RuntimeException("調用發送驗證碼過于頻繁,請稍后再試!"); }
-
后端增加每天次數校驗,每個電話號碼每天只能發送50次
private static final int MAX_DAILY_SMS = 50; // 每日最大短信發送數量// 獲取當前日期作為短信發送統計的鍵 String dailySmsKey = getDailySmsCountKey(LocalDate.now(),mobile);// 檢查每日短信發送數量是否超出限制 long currentDaySmsCount = redisTemplate.opsForValue().increment(dailySmsKey,1);//設置過期時間為當天23:59:59 LocalDateTime todayEnd = LocalDateTime.of(LocalDate.now(), LocalTime.of(23, 59, 59)); long secondsUntilMidnight = ChronoUnit.SECONDS.between(LocalDateTime.now(), todayEnd); redisTemplate.expire(dailySmsKey, secondsUntilMidnight, TimeUnit.SECONDS); if (currentDaySmsCount > MAX_DAILY_SMS) {LOGGER.error("今日短信發送已達上限,無法再發送給 {}", mobile);throw new RuntimeException("當日短信發送已達上限,無法再發送,請明日再試!"); }private String getDailySmsCountKey(LocalDate date,String mobile) {return "sms_daily_count:" +mobile + date.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));}
-
對每日超過50次的ip記錄下來,也可以加入黑名單,下次判斷請求ip為黑名單中的ip則直接返回失敗
-
前后端增加一個對稱加密的校驗碼
準備:密鑰 1234abcd;約定明文中必須包含指定字符串miyao1234
1.前端調用發送短信接口前,通過密鑰(1234abcd)與加密算法,將miyao1234+(16位英文字母與數字字符串) 例如miyao1234wgly1noKSXg47Mn6 進行加密
生成校驗碼 vBOLxJj3wl1IyJNUcXOPvaeXvgLZK0b4f3D4J6k9DvE=
2.前端調用發送短信接口時,將校驗碼傳遞給后端
3.后端接收到后對校驗碼進行解密,如果解密失敗,提示失敗
4.如果解密后不包含約定字符串miyao1234 ,提示失敗
5.如果解密后包含約定字符串miyao1234,則通過校驗,此時注意??,需要將此校驗碼存入redis,下次如果有相同校驗碼 則提示重復
/*** 解密后的驗證碼必須包含此值*/public static final String ENCRYPT_KEY_MAIN_WORD = "miyao1234";//密鑰1234abcdpublic static final String encryptKey = "1234abcd";Validate.notBlank(keyDecrypt,"發送短信時,校驗碼不能為空"); //進行解密 String decrypt = this.decrypt(keyDecrypt); LOGGER.error("發送驗證碼解密后的校驗碼"+decrypt); if(StringUtils.isEmpty(decrypt) || !decrypt.contains(ENCRYPT_KEY_MAIN_WORD)){throw new RuntimeException("發送驗證碼校驗失敗"); } //設置過期時間為當天23:59:59 // 每個校驗碼每天只能重復使用一次 String repeatLock = StringUtils.join("decrypt_send_sms:", decrypt); LocalDateTime todayEnd = LocalDateTime.of(LocalDate.now(), LocalTime.of(23, 59, 59)); long secondsUntilMidnight = ChronoUnit.SECONDS.between(LocalDateTime.now(), todayEnd); // 使用 Redis 的 setIfAbsent 進行原子性操作,嘗試設置鍵值對,如果鍵已存在則返回 false if (!redisTemplate.opsForValue().setIfAbsent(repeatLock, "locked", secondsUntilMidnight, TimeUnit.SECONDS)) {// 鍵已存在,表示校驗碼重復throw new RuntimeException("校驗碼重復!"); }public String decrypt(String decrypt) {if (StringUtils.isBlank(decrypt)) {return decrypt;}return Aes128Utils.decrypt(decrypt, encryptKey, Aes128Utils.EncodeType.CBC, Aes128Utils.Padding.PKCS_7_PADDING);}