SpringBoot2.x 集成騰訊云短信的詳細(xì)流程
一、騰訊云短信簡介
騰訊云短信(Short Message Service,SMS)沉淀騰訊十多年短信服務(wù)技術(shù)和經(jīng)驗(yàn),為QQ、微信等億級平臺(tái)和10萬+客戶提供快速靈活接入的高質(zhì)量的國內(nèi)短信與國際/港澳臺(tái)短信服務(wù)。
- 國內(nèi)短信驗(yàn)證秒級觸達(dá),99%到達(dá)率。
- 國際/港澳臺(tái)短信覆蓋全球200+國家/地區(qū),穩(wěn)定可靠。
單次短信的業(yè)務(wù)請求流程如下所示:
短信由簽名和正文內(nèi)容組成,發(fā)送短信前需要申請短信簽名和正文內(nèi)容模板。短信簽名是位于短信正文前【】中的署名,用于標(biāo)識(shí)公司或業(yè)務(wù)。短信簽名需要審核通過后才可使用。短信模板即具體發(fā)送的短信正文內(nèi)容,短信模板支持驗(yàn)證碼模板、通知類短信模板和營銷短信模板。短信內(nèi)容可以通過模板參數(shù)實(shí)現(xiàn)個(gè)性化定制。短信模板申請前需要先申請短信簽名,短信模板需要審核通過后才可使用。
二、準(zhǔn)備工作
1.開通短信服務(wù)
如果沒有騰訊云賬號(hào),需要注冊騰訊云賬號(hào),并完成實(shí)名認(rèn)證,可以個(gè)人認(rèn)證和企業(yè)認(rèn)證,不能進(jìn)行企業(yè)認(rèn)證的話也可以進(jìn)行個(gè)人認(rèn)證。然后進(jìn)入騰訊云短信控制臺(tái),開通短信服務(wù),開通短信和個(gè)人認(rèn)證之后分別都會(huì)贈(zèng)送包含100條短信的國內(nèi)套餐包,用來測試足夠:
2.創(chuàng)建簽名
這里創(chuàng)建國內(nèi)短信簽名,創(chuàng)建完成后等到狀態(tài)變?yōu)橐淹ㄟ^就可以使用了:
創(chuàng)建簽名時(shí)簽名類型可以選網(wǎng)站、APP、公眾號(hào)和小程序,可以根據(jù)需要?jiǎng)?chuàng)建:
3.創(chuàng)建正文模板
創(chuàng)建模板,創(chuàng)建完成之后狀態(tài)變?yōu)橐淹ㄟ^就可以使用了:
模板內(nèi)容可以使用標(biāo)準(zhǔn)模板也可以自定義:
4.創(chuàng)建短信應(yīng)用
在應(yīng)用列表下可以創(chuàng)建短信應(yīng)用,獲取短信應(yīng)用的SDKAppID:
點(diǎn)擊應(yīng)用可以查看應(yīng)用信息:
5.騰訊云API密鑰
在訪問管理菜單的訪問密鑰下的API密鑰管理中可以新建和查看API密鑰,在請求騰訊云短信服務(wù)發(fā)送短信時(shí)需要傳入該密鑰:
三、集成騰訊云短信
通過Maven新建一個(gè)名為springboot-tencent-sms
的項(xiàng)目。
1.引入依賴
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- 騰訊云 Java SDK 依賴 --> <dependency> <groupId>com.tencentcloudapi</groupId> <artifactId>tencentcloud-sdk-java</artifactId> <version>3.1.297</version> </dependency> <!-- Spring Data Redis 起步依賴 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <!-- lombok插件 --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.8</version> </dependency>
2.編寫配置類
用于讀取配置文件中的自定義騰訊云短信配置的配置類:
package com.rtxtitanv.config; import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Configuration; /** * @author rtxtitanv * @version 1.0.0 * @name com.rtxtitanv.config.SmsConfig * @description 騰訊云短信配置類 * @date 2021/6/25 16:21 */ @ConfigurationProperties(prefix = "tencent.sms") @Configuration @Data public class SmsConfig { /** * 騰訊云API密鑰的SecretId */ private String secretId; /** * 騰訊云API密鑰的SecretKey */ private String secretKey; /** * 短信應(yīng)用的SDKAppID */ private String appId; /** * 簽名內(nèi)容 */ private String sign; /** * 模板ID */ private String templateId; /** * 過期時(shí)間 */ private String expireTime; /** * redis存儲(chǔ)的key的前綴 */ private String phonePrefix; }
Redis配置類:
package com.rtxtitanv.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer; import javax.annotation.Resource; /** * @author rtxtitanv * @version 1.0.0 * @name com.rtxtitanv.config.RedisConfig * @description Redis配置類 * @date 2021/6/26 12:24 */ @Configuration public class RedisConfig { @Resource private RedisConnectionFactory redisConnectionFactory; /** * RedisTemplate實(shí)例 * * @return RedisTemplate實(shí)例 */ @Bean public RedisTemplate<String, Object> redisTemplate() { RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>(); initRedisTemplate(redisTemplate, redisConnectionFactory); return redisTemplate; } /** * 設(shè)置數(shù)據(jù)存入redis的序列化方式 * * @param redisTemplate RedisTemplate對象 * @param factory RedisConnectionFactory對象 */ private void initRedisTemplate(RedisTemplate<String, Object> redisTemplate, RedisConnectionFactory factory) { redisTemplate.setKeySerializer(new StringRedisSerializer()); redisTemplate.setValueSerializer(new StringRedisSerializer()); redisTemplate.setHashKeySerializer(new GenericJackson2JsonRedisSerializer()); redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer()); redisTemplate.setConnectionFactory(factory); } }
3.編寫配置文件
spring: redis: host: 127.0.0.1 port: 6379 # 自定義騰訊云短信配置 tencent: sms: # 配置騰訊云API密鑰的SecretId secretId: 這里填騰訊云API密鑰的SecretId # 配置騰訊云API密鑰的SecretKey secretKey: 這里填騰訊云API密鑰的SecretKey # 配置短信應(yīng)用的SDKAppID appId: 這里填短信應(yīng)用的SDKAppID # 配置簽名內(nèi)容 sign: "這里填簽名內(nèi)容" # 配置模板ID templateId: 這里填模板ID # 配置過期時(shí)間 expireTime: 5 # 配置redis存儲(chǔ)的key的前綴 phonePrefix: REGIST
4.編寫工具類
騰訊云短信工具類:
package com.rtxtitanv.util; import com.rtxtitanv.config.SmsConfig; import com.tencentcloudapi.common.Credential; import com.tencentcloudapi.common.exception.TencentCloudSDKException; import com.tencentcloudapi.common.profile.ClientProfile; import com.tencentcloudapi.common.profile.HttpProfile; import com.tencentcloudapi.sms.v20210111.SmsClient; import com.tencentcloudapi.sms.v20210111.models.SendSmsRequest; import com.tencentcloudapi.sms.v20210111.models.SendSmsResponse; import com.tencentcloudapi.sms.v20210111.models.SendStatus; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author rtxtitanv * @version 1.0.0 * @name com.rtxtitanv.util.SmsUtil * @description 騰訊云短信工具類 * @date 2021/6/25 16:21 */ public class SmsUtil { private static final Logger LOGGER = LoggerFactory.getLogger(SmsUtil.class); /** * 發(fā)送短信 * * @param smsConfig 騰訊云短信配置對象 * @param templateParams 模板參數(shù) * @param phoneNumbers 手機(jī)號(hào)數(shù)組 * @return SendStatus[],短信發(fā)送狀態(tài) */ public static SendStatus[] sendSms(SmsConfig smsConfig, String[] templateParams, String[] phoneNumbers) { try { // 實(shí)例化一個(gè)認(rèn)證對象,入?yún)⑿枰獋魅腧v訊云賬戶密鑰對secretId,secretKey。 Credential cred = new Credential(smsConfig.getSecretId(), smsConfig.getSecretKey()); // 實(shí)例化一個(gè)http選項(xiàng),可選,沒有特殊需求可以跳過 HttpProfile httpProfile = new HttpProfile(); // SDK默認(rèn)使用POST方法 httpProfile.setReqMethod("POST"); // SDK有默認(rèn)的超時(shí)時(shí)間,非必要請不要進(jìn)行調(diào)整 httpProfile.setConnTimeout(60); // 非必要步驟:實(shí)例化一個(gè)客戶端配置對象,可以指定超時(shí)時(shí)間等配置 ClientProfile clientProfile = new ClientProfile(); // SDK默認(rèn)用TC3-HMAC-SHA256進(jìn)行簽名,非必要請不要修改這個(gè)字段 clientProfile.setSignMethod("HmacSHA256"); clientProfile.setHttpProfile(httpProfile); // 實(shí)例化要請求產(chǎn)品(以sms為例)的client對象,第二個(gè)參數(shù)是地域信息,可以直接填寫字符串a(chǎn)p-guangzhou,或者引用預(yù)設(shè)的常量 SmsClient smsClient = new SmsClient(cred, "ap-guangzhou", clientProfile); // 實(shí)例化一個(gè)請求對象 SendSmsRequest req = new SendSmsRequest(); // 設(shè)置短信應(yīng)用ID:短信SdkAppId在[短信控制臺(tái)]添加應(yīng)用后生成的實(shí)際SdkAppId req.setSmsSdkAppId(smsConfig.getAppId()); // 設(shè)置短信簽名內(nèi)容:使用UTF-8編碼,必須填寫已審核通過的簽名,簽名信息可登錄[短信控制臺(tái)]查看 req.setSignName(smsConfig.getSign()); // 設(shè)置國際/港澳臺(tái)短信SenderId:國內(nèi)短信填空,默認(rèn)未開通 req.setSenderId(""); // 設(shè)置模板ID:必須填寫已審核通過的模板ID。模板ID可登錄[短信控制臺(tái)]查看 req.setTemplateId(smsConfig.getTemplateId()); // 設(shè)置下發(fā)手機(jī)號(hào)碼,采用E.164標(biāo)準(zhǔn),+[國家或地區(qū)碼][手機(jī)號(hào)] req.setPhoneNumberSet(phoneNumbers); // 設(shè)置模板參數(shù):若無模板參數(shù),則設(shè)置為空 req.setTemplateParamSet(templateParams); // 通過client對象調(diào)用SendSms方法發(fā)起請求。注意請求方法名與請求對象是對應(yīng)的,返回的res是一個(gè)SendSmsResponse類的實(shí)例,與請求對象對應(yīng) SendSmsResponse res = smsClient.SendSms(req); // 控制臺(tái)打印日志輸出json格式的字符串回包 LOGGER.info(SendSmsResponse.toJsonString(res)); return res.getSendStatusSet(); } catch (TencentCloudSDKException e) { e.printStackTrace(); throw new RuntimeException(e.getMessage()); } } }
Redis工具類:
package com.rtxtitanv.util; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Component; import javax.annotation.Resource; import java.util.concurrent.TimeUnit; /** * @author rtxtitanv * @version 1.0.0 * @name com.rtxtitanv.util.RedisUtil * @description Redis工具類 * @date 2021/6/26 12:24 */ @Component public class RedisUtil { @Resource private RedisTemplate<String, Object> redisTemplate; /** * 緩存基本對象 * * @param key 鍵 * @param value 值 * @param expire 鍵的過期時(shí)間 */ public void setCacheObject(String key, Object value, long expire) { redisTemplate.opsForValue().set(key, value); if (expire > 0) { redisTemplate.expire(key, expire, TimeUnit.MINUTES); } } /** * 獲取指定鍵的緩存對象 * * @param key 鍵 * @return 緩存對象 */ public Object getCacheObject(String key) { return redisTemplate.opsForValue().get(key); } /** * 判斷鍵是否存在并且未過期 * * @param key 鍵 * @return true,鍵存在并且未過期;false,鍵不存在或存在但過期 */ public boolean hasKey(String key) { return redisTemplate.hasKey(key) && getExpire(key) > 0 ? true : false; } /** * 獲取鍵的過期時(shí)間 * * @param key 鍵 * @return 過期時(shí)間 */ public long getExpire(String key) { return redisTemplate.getExpire(key, TimeUnit.MINUTES); } /** * 刪除指定鍵的緩存 * * @param key 鍵 */ public void delete(String key) { redisTemplate.delete(key); } /** * 創(chuàng)建緩存的鍵 * * @param prefix 前綴 * @param phoneNumber 手機(jī)號(hào) * @return 鍵 */ public static String createCacheKey(String prefix, String phoneNumber) { return prefix + phoneNumber; } }
用于生成隨機(jī)驗(yàn)證碼的工具類:
package com.rtxtitanv.util; import java.util.Random; /** * @author rtxtitanv * @version 1.0.0 * @name com.rtxtitanv.util.RandomUtil * @description Random工具類 * @date 2021/6/26 11:39 */ public class RandomUtil { private static final Random RANDOM = new Random(); /** * 生成指定位數(shù)的隨機(jī)數(shù)字字符串 * * @param length 字符串長度 * @return 隨機(jī)數(shù)字字符串 */ public static String randomNumbers(int length) { StringBuilder randomNumbers = new StringBuilder(); for (int i = 0; i < length; i++) { randomNumbers.append(RANDOM.nextInt(10)); } return randomNumbers.toString(); } }
5.Controller層
package com.rtxtitanv.controller; import com.rtxtitanv.service.SmsService; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import javax.annotation.Resource; /** * @author rtxtitanv * @version 1.0.0 * @name com.rtxtitanv.controller.SmsController * @description SmsController * @date 2021/6/25 16:20 */ @RequestMapping("/sms") @RestController public class SmsController { @Resource private SmsService smsService; @PostMapping("/send") public String sendSmsCode(@RequestParam(value = "phoneNumber") String phoneNumber) { return smsService.sendSmsCode(phoneNumber); } @PostMapping("/verify") public String verifySmsCode(@RequestParam(value = "phoneNumber") String phoneNumber, @RequestParam(value = "smsCode") String smsCode) { return smsService.verifySmsCode(phoneNumber, smsCode); } }
6.Service層
package com.rtxtitanv.service; /** * @author rtxtitanv * @version 1.0.0 * @name com.rtxtitanv.service.SmsService * @description SmsService * @date 2021/6/25 16:20 */ public interface SmsService { /** * 發(fā)送短信驗(yàn)證碼 * * @param phoneNumber 手機(jī)號(hào) * @return */ String sendSmsCode(String phoneNumber); /** * 驗(yàn)證短信驗(yàn)證碼 * * @param phoneNumber 手機(jī)號(hào) * @param smsCode 短信驗(yàn)證碼 * @return */ String verifySmsCode(String phoneNumber, String smsCode); }
package com.rtxtitanv.service.impl; import com.rtxtitanv.config.SmsConfig; import com.rtxtitanv.service.SmsService; import com.rtxtitanv.util.RandomUtil; import com.rtxtitanv.util.RedisUtil; import com.rtxtitanv.util.SmsUtil; import com.tencentcloudapi.sms.v20210111.models.SendStatus; import org.springframework.stereotype.Service; import javax.annotation.Resource; /** * @author rtxtitanv * @version 1.0.0 * @name com.rtxtitanv.service.impl.SmsServiceImpl * @description SmsService實(shí)現(xiàn)類 * @date 2021/6/25 16:20 */ @Service public class SmsServiceImpl implements SmsService { @Resource private SmsConfig smsConfig; @Resource private RedisUtil redisUtil; @Override public String sendSmsCode(String phoneNumber) { // 下發(fā)手機(jī)號(hào)碼,采用e.164標(biāo)準(zhǔn),+[國家或地區(qū)碼][手機(jī)號(hào)] String[] phoneNumbers = {"+86" + phoneNumber}; // 生成6位隨機(jī)數(shù)字字符串 String smsCode = RandomUtil.randomNumbers(6); // 模板參數(shù):若無模板參數(shù),則設(shè)置為空(參數(shù)1為隨機(jī)驗(yàn)證碼,參數(shù)2為有效時(shí)間) String[] templateParams = {smsCode, smsConfig.getExpireTime()}; // 發(fā)送短信驗(yàn)證碼 SendStatus[] sendStatuses = SmsUtil.sendSms(smsConfig, templateParams, phoneNumbers); if ("Ok".equals(sendStatuses[0].getCode())) { // 創(chuàng)建緩存的key String key = RedisUtil.createCacheKey(smsConfig.getPhonePrefix(), phoneNumber); // 將驗(yàn)證碼緩存到redis并設(shè)置過期時(shí)間 redisUtil.setCacheObject(key, smsCode, Long.parseLong(smsConfig.getExpireTime())); return "驗(yàn)證碼發(fā)送成功"; } else { return "驗(yàn)證碼發(fā)送失敗:" + sendStatuses[0].getMessage(); } } @Override public String verifySmsCode(String phoneNumber, String smsCode) { // 創(chuàng)建key String key = RedisUtil.createCacheKey(smsConfig.getPhonePrefix(), phoneNumber); // 判斷指定key是否存在并且未過期 if (redisUtil.hasKey(key)) { // 驗(yàn)證輸入的驗(yàn)證碼是否正確 if (smsCode.equals(redisUtil.getCacheObject(key))) { // 驗(yàn)證成功后刪除驗(yàn)證碼緩存 redisUtil.delete(key); return "驗(yàn)證成功"; } else { return "驗(yàn)證碼錯(cuò)誤"; } } else { return "驗(yàn)證碼已失效"; } } }
四、騰訊云短信測試
運(yùn)行項(xiàng)目,使用Postman進(jìn)行接口測試。
1.發(fā)送短信驗(yàn)證碼
發(fā)送如下POST請求以請求騰訊云短信服務(wù)向指定手機(jī)號(hào)發(fā)送短信驗(yàn)證碼,請求地址為http://localhost:8080/sms/send
:
Redis客戶端查看發(fā)現(xiàn)驗(yàn)證碼已經(jīng)存到了Redis中:
手機(jī)上也成功收到了驗(yàn)證碼,說明發(fā)送短信成功:
2.驗(yàn)證短信驗(yàn)證碼
發(fā)送如下POST請求驗(yàn)證短信驗(yàn)證碼,請求地址為http://localhost:8080/sms/verify
,這里將驗(yàn)證碼參數(shù)smscode
設(shè)為不同于之前發(fā)送到手機(jī)的驗(yàn)證碼來模擬驗(yàn)證碼輸入錯(cuò)誤:
再次發(fā)送如下POST請求驗(yàn)證短信驗(yàn)證碼,這里輸入正確的驗(yàn)證碼:
Redis客戶端刷新發(fā)現(xiàn)驗(yàn)證碼緩存已經(jīng)刪除:
再次發(fā)送如下POST請求驗(yàn)證短信驗(yàn)證碼,發(fā)現(xiàn)驗(yàn)證碼已失效,滿足短信驗(yàn)證碼驗(yàn)證成功后就失效的業(yè)務(wù)需求:
代碼示例
Github:https://github.com/RtxTitanV/springboot-learning/tree/master/springboot2.x-learning/springboot-tencent-sms
Gitee:https://gitee.com/RtxTitanV/springboot-learning/tree/master/springboot2.x-learning/springboot-tencent-sms
到此這篇關(guān)于SpringBoot2.x 集成騰訊云短信的文章就介紹到這了,更多相關(guān)SpringBoot騰訊云短信內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot 如何自定義項(xiàng)目啟動(dòng)信息打印
這篇文章主要介紹了SpringBoot 如何自定義項(xiàng)目啟動(dòng)信息打印方式,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-09-09Java8優(yōu)雅的字符串拼接工具類StringJoiner實(shí)例代碼
這篇文章主要給大家介紹了關(guān)于Java8優(yōu)雅的字符串拼接工具類StringJoiner的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-02-02Spring Cloud Zuul自定義過濾器的實(shí)現(xiàn)
這篇文章主要介紹了自定義Spring Cloud Zuul過濾器的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-03-03Java線程中斷機(jī)制interrupt、isInterrupted、interrupted方法詳解
這篇文章主要介紹了Java線程中斷機(jī)制interrupt、isInterrupted、interrupted方法詳解,一個(gè)線程不應(yīng)該由其他線程來強(qiáng)制中斷或停止,而是應(yīng)該由線程自己自行停止,所以,Thread.stop、Thread.suspend、Thread. resume都已經(jīng)被廢棄了,需要的朋友可以參考下2024-01-01詳解SpringBoot靜態(tài)方法獲取bean的三種方式
本文主要介紹了詳解SpringBoot靜態(tài)方法獲取bean的三種方式,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-10-10Java面試題之HashSet的實(shí)現(xiàn)原理
這篇文章主要介紹了Java面試題之HashSet的實(shí)現(xiàn)原理,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-01-01使用maven實(shí)現(xiàn)有關(guān)Jsoup簡單爬蟲的步驟
這篇文章主要介紹了使用maven實(shí)現(xiàn)有關(guān)Jsoup簡單爬蟲的步驟,文中附含詳細(xì)示例代碼,有需要的朋友可以借鑒參考下,希望能夠有所幫助2021-09-09Java中g(shù)etResourceAsStream用法分析
這篇文章主要介紹了Java中g(shù)etResourceAsStream用法,較為詳細(xì)的分析了getResourceAsStream的功能及用法,需要的朋友可以參考下2015-06-06java讀取文件顯示進(jìn)度條的實(shí)現(xiàn)方法
當(dāng)讀取一個(gè)大文件時(shí),一時(shí)半會(huì)兒無法看到讀取結(jié)果,就需要顯示一個(gè)進(jìn)度條,是程序員明白已經(jīng)讀了多少文件,可以估算讀取還需要多少時(shí)間,下面的代碼可以實(shí)現(xiàn)這個(gè)功能2014-01-01