java短信驗證碼登錄功能設計與實現
前言
現在不管是各類的網站,還是大小社交app,登錄方式是越來越多瞭,其中基於短信驗證碼的登錄可以說是各類app必不可少的方式,短信驗證碼登錄以其高效,安全,便捷等特性受到許多用戶的青睞
業務案例
如下所示,是一個大傢熟知的采用短信登錄的入口
輸入手機號之後,出現如下效果,
輸入手機上面收到的驗證碼之後,就可以正常登錄瞭
業務關鍵點剖析
以上是一個正常的使用短信驗證碼登錄的業務流程,在實際開發中,需要考慮的因素更多瞭,比如:
- 驗證碼位數如何
- 驗證碼如何存儲
- 如何預防短信被刷
- 倒計時功能,前後端如何配合
其實來說,短信驗證碼功能並不難,難得是如何做到業務場景的全面覆蓋和功能細節上面的考慮
短信驗證碼功能實現思路
小編結合實際經驗和調研,目前比較流行的做法是,使用redis做短信驗證碼,想必說到這裡,懂行的同學們應該猜到瞭
完整的業務邏輯大概如下:
依據這個業務邏輯的實現思路,我們大致可以理清代碼的編寫邏輯,在小編開發過程中,其中有一個點遇到瞭一點梗,就是關於驗證碼的有效期的問題,主要考慮下面2點:
- 後端存儲驗證碼有效期時長
- 前端頁面倒計時和後端有效期的關系
有效期問題
下面我們編寫代碼來演示下完整的過程
前置準備:搭建一個springboot工程
操作步驟
1、導入核心依賴
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <version>2.2.1.RELEASE</version> </dependency>
2、編寫獲取短信驗證碼方法
@Service public class SmsServiceImpl implements SmsService { public static final String VERIFY_CODE = "login:verify_code:"; @Autowired private DbUserMapper dbUserMapper; @Autowired private RedisTemplate<String,String> redisTemplate; @Override public String getSmsVerifyCode(String phone) { if (StringUtils.isEmpty(phone)) { throw new RuntimeException("用戶手機號為空"); } QueryWrapper queryWrapper = new QueryWrapper(); queryWrapper.eq("mobile",phone); DbUser dbUser = dbUserMapper.selectOne(queryWrapper); if(dbUser == null){ throw new RuntimeException("用戶不存在"); } String smsVerifyCode = getSmsVerifyCode(); String smsCodeKey = VERIFY_CODE + dbUser.getUserId(); String existedSmsCode = redisTemplate.opsForValue().get(smsCodeKey); //如果驗證碼已經存在時 if (StringUtils.isNotEmpty(existedSmsCode)) { Long expireTime = redisTemplate.opsForValue().getOperations().getExpire(smsCodeKey); long lastTime = 60 * 3 - expireTime; //三分鐘內驗證碼有效,1分鐘到3分鐘之間,用戶可以繼續輸入驗證碼,也可以重新獲取驗證碼,新的驗證碼將覆蓋舊的 if(lastTime > 60 && expireTime >0){ //調用第三方平臺發短信,隻有短信發送成功瞭,才能將短信驗證碼保存到redis System.out.println("此處調用短信發送邏輯......"); redisTemplate.opsForValue().set(smsCodeKey, smsVerifyCode, 60 * 3, TimeUnit.SECONDS); System.out.println("短信驗證碼:" + smsVerifyCode); } //一分鐘之內不得多次獲取驗證碼 if(lastTime < 60){ throw new RuntimeException("操作過於頻繁,請一分鐘之後再次點擊發送"); } }else { //調用notify服務發送短信,隻有notify的短信發送成功瞭,才能將短信驗證碼保存到redis System.out.println("此處調用短信發送邏輯......"); System.out.println("短信驗證碼:" + smsVerifyCode); redisTemplate.opsForValue().set(smsCodeKey, smsVerifyCode, 60 * 3, TimeUnit.SECONDS); } return smsVerifyCode; } /** * 隨機獲取6位短信數字驗證碼 * * @return */ public static String getSmsVerifyCode() { Random random = new Random(); String code = ""; for (int i = 0; i < 6; i++) { int rand = random.nextInt(10); code += rand; } return code; } }
發送短信驗證碼需充分考慮幾個場景:
- 首次輸入手機號,獲取驗證碼時,後端設置驗證碼有效期為3分鐘,前端倒計時1分鐘
- 1分鐘之內,用戶不能第二次獲取驗證碼,1分鐘之後,用戶可以重新獲取驗證碼
- 超過1分鐘後,同一個用戶再次點擊獲取驗證碼時,後端需主動移除redis存儲的上一次驗證碼
- 3分鐘有效期內,用戶可隨時輸入第一次的驗證碼進行登錄
- 登錄成功後,登錄接口需主動移除redis中的驗證碼
以上為驗證碼的核心業務方法,下面再編寫一個登錄的方法,當基礎校驗和驗證碼校驗通過後,即可登錄
@Override public String login(String userId, String smsCode) { if (StringUtils.isEmpty(userId)) { throw new RuntimeException("用戶ID必傳"); } if (StringUtils.isEmpty(smsCode)) { throw new RuntimeException("驗證碼不能為空"); } QueryWrapper queryWrapper = new QueryWrapper(); queryWrapper.eq("user_id",userId); DbUser dbUser = dbUserMapper.selectOne(queryWrapper); if(dbUser == null){ throw new RuntimeException("用戶不存在"); } //校驗驗證碼 String smsCodeKey = VERIFY_CODE + dbUser.getUserId(); String verifyCode = redisTemplate.opsForValue().get(smsCodeKey); if (StringUtils.isEmpty(verifyCode)) { throw new RuntimeException("短信驗證碼不存在或已過期"); } if (!StringUtils.equals(smsCode, verifyCode)) { throw new RuntimeException("短信驗證碼錯誤"); } //TODO 其他待驗證的登錄業務邏輯 System.out.println("執行其他業務......"); System.out.println("登錄成功"); //最後清理下驗證碼 if(redisTemplate.hasKey(smsCodeKey)){ redisTemplate.delete(smsCodeKey); } return "login success"; }
3、提供獲取驗證碼和登錄接口
@RestController public class SmsController { @Autowired private SmsService smsService; @GetMapping("get/sms_code") public String getSmsVerifyCode(@RequestParam("phone") String phone){ return smsService.getSmsVerifyCode(phone); } /** * 登錄 * @param userId * @param smsCode * @return */ @GetMapping("/login") public String login(@RequestParam("userId") String userId,@RequestParam("smsCode") String smsCode){ return smsService.login(userId,smsCode); } }
啟動redis服務,啟動項目,數據庫提前準備一條數據
場景測試1:獲取驗證碼
調用登錄接口,使用上面的驗證碼:
場景測試2:1分鐘內多次獲取驗證碼
第一次獲取驗證碼
再次獲取驗證碼
超過1分鐘少於3分鐘內,再次獲取驗證碼,得到新的驗證碼,同時redis中存儲的是最新的驗證碼
場景測試3:登錄輸入錯誤驗證碼
總體來說,使用redis實現短信驗證碼登錄的功能不算太復雜,主要是需要全面的考慮到各自使用場景即可
到此這篇關於java短信驗證碼登錄功能設計與實現的文章就介紹到這瞭,更多相關java短信驗證碼登錄內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- SpringBoot詳解如何整合Redis緩存驗證碼
- Redis鎖完美解決高並發秒殺問題
- Redis高級數據類型Hyperloglog、Bitmap的使用
- 聊聊使用RedisTemplat實現簡單的分佈式鎖的問題
- Redis實現短信驗證碼登錄的示例代碼