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!

推薦閱讀: