一、背景
本項目基于RuoYi 3.8.9前后端分離框架構建,采用Spring Security實現系統權限管理。作為企業級應用架構的子模塊,系統需要與頂層項目實現用戶數據無縫對接(以手機號作為統一用戶標識),同時承擔用戶信息采集的重要職能。為此,我們在保留原有賬號密碼登錄方式的基礎上,創新性地集成了手機號驗證碼登錄/注冊功能,既滿足了企業級用戶管理的標準化需求,又優化了終端用戶的使用體驗。
二、短信集成
短信集成可以直接使用短信供應商的SDK,公司目前采購的阿里云短信,短信集成可以直接參照阿里云短信官方文檔。當然也可以采用其他更通用一點的集成方式,本人秉持著不重復造輪子同時方便后期短信供應商的變更不再次添加供應商代碼,直接采用開源的短信集成工具SM4J,需要了解的可以查看SMS4J官方文檔,集成過程如下:
-
1.添加maven依賴,直接上最新的發布版本:
<dependency><groupId>org.dromara.sms4j</groupId><artifactId>sms4j-spring-boot-starter</artifactId><version>3.3.5</version></dependency>
-
2.添加短信配置:
#短信配置 sms:# 標注從yml讀取配置config-type: yamlHttpLog: truecorePoolSize: 2maxPoolSize: 6queueCapacity: 200blends:# 自定義的標識,也就是configId這里可以是任意值(最好不要是中文)alibaba:#框架定義的廠商名稱標識supplier: alibaba#有些稱為accessKey有些稱之為apiKey,也有稱為sdkKey或者appId。access-key-id: #稱為accessSecret有些稱之為apiSecret。access-key-secret:#您的短信簽名signature: #模板ID 如果不需要簡化的sendMessage方法可以不配置template-id: # 隨機權重,負載均衡的權重值依賴于此,默認為1,如不需要負載均衡可不進行配置weight: 1#配置標識名稱 如果你使用的yml進行配置,則blends下層配置的就是這個,可為空,如果你使用的接口類配置,則需要設置值#需要注意的是,不同的配置之間config-id不能重復,否則會發生配置丟失的問題config-id: alibaba#短信自動重試間隔時間 默認五秒retry-interval: 10# 短信重試次數,默認0次不重試,如果你需要短信重試則根據自己的需求修改值即可max-retries: 2
-
3.短信發送工具:
package com.book.framework.sms;import com.book.common.constant.CacheConstants; import com.book.common.core.redis.RedisCache; import lombok.extern.slf4j.Slf4j; import org.dromara.sms4j.api.entity.SmsResponse; import org.dromara.sms4j.core.factory.SmsFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service;import java.util.LinkedHashMap; import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.TimeUnit;/*** @className: SmsService* @author: liuyh* @date: 2025/5/21 17:57* @Version: 1.0*/ @Slf4j @Service public class SmsService {/*** 短信服務提供商* {@value CONFIG_ID}*/private static final String CONFIG_ID = "alibaba";@Autowiredprivate RedisCache redisCache;/*** 發送短信** @param phoneNumber* @param message* @return*/public boolean sendSms(String phoneNumber, String message) {SmsResponse smsResponse = SmsFactory.getSmsBlend(CONFIG_ID).sendMessage(phoneNumber, message);boolean beSent = smsResponse.isSuccess();if (!beSent) {log.info("短信服務商錯誤響應原始消息體: {}", smsResponse.getData());}return beSent;}/*** 發送短信** @param phoneNumber* @param messages* @return*/public boolean sendSms(String phoneNumber, LinkedHashMap<String, String> messages) {SmsResponse smsResponse = SmsFactory.getSmsBlend(CONFIG_ID).sendMessage(phoneNumber, messages);boolean beSent = smsResponse.isSuccess();if (!beSent) {log.info("短信服務商錯誤響應原始消息體: {}", smsResponse.getData());}return smsResponse.isSuccess();}/*** 發送手機驗證方法** @param phoneNumber* @return*/public boolean sendVerificationCode(String phoneNumber) {String code = this.generateAndStoreCode(phoneNumber);LinkedHashMap<String, String> messages = new LinkedHashMap<>();messages.put("code", code);return this.sendSms(phoneNumber, messages);}/*** 生成6位隨機驗證碼并存入Redis* <br>* <b>默認5分鐘過期</b>** @param phoneNumber 手機號* @return 生成的驗證碼*/private String generateAndStoreCode(Str