SpringBoot2.x 集成腾讯云短信的详细流程
作者:RtxTitanV
一、腾讯云短信简介
腾讯云短信(Short Message Service,SMS)沉淀腾讯十多年短信服务技术和经验,为QQ、微信等亿级平台和10万+客户提供快速灵活接入的高质量的国内短信与国际/港澳台短信服务。
- 国内短信验证秒级触达,99%到达率。
- 国际/港澳台短信覆盖全球200+国家/地区,稳定可靠。
单次短信的业务请求流程如下所示:
短信由签名和正文内容组成,发送短信前需要申请短信签名和正文内容模板。短信签名是位于短信正文前【】中的署名,用于标识公司或业务。短信签名需要审核通过后才可使用。短信模板即具体发送的短信正文内容,短信模板支持验证码模板、通知类短信模板和营销短信模板。短信内容可以通过模板参数实现个性化定制。短信模板申请前需要先申请短信签名,短信模板需要审核通过后才可使用。
二、准备工作
1.开通短信服务
如果没有腾讯云账号,需要注册腾讯云账号,并完成实名认证,可以个人认证和企业认证,不能进行企业认证的话也可以进行个人认证。然后进入腾讯云短信控制台,开通短信服务,开通短信和个人认证之后分别都会赠送包含100条短信的国内套餐包,用来测试足够:
2.创建签名
这里创建国内短信签名,创建完成后等到状态变为已通过就可以使用了:
创建签名时签名类型可以选网站、APP、公众号和小程序,可以根据需要创建:
3.创建正文模板
创建模板,创建完成之后状态变为已通过就可以使用了:
模板内容可以使用标准模板也可以自定义:
4.创建短信应用
在应用列表下可以创建短信应用,获取短信应用的SDKAppID:
点击应用可以查看应用信息:
5.腾讯云API密钥
在访问管理菜单的访问密钥下的API密钥管理中可以新建和查看API密钥,在请求腾讯云短信服务发送短信时需要传入该密钥:
三、集成腾讯云短信
通过Maven新建一个名为springboot-tencent-sms
的项目。
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; /** * 短信应用的SDKAppID */ private String appId; /** * 签名内容 */ private String sign; /** * 模板ID */ private String templateId; /** * 过期时间 */ private String expireTime; /** * redis存储的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实例 * * @return RedisTemplate实例 */ @Bean public RedisTemplate<String, Object> redisTemplate() { RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>(); initRedisTemplate(redisTemplate, redisConnectionFactory); return redisTemplate; } /** * 设置数据存入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 # 配置短信应用的SDKAppID appId: 这里填短信应用的SDKAppID # 配置签名内容 sign: "这里填签名内容" # 配置模板ID templateId: 这里填模板ID # 配置过期时间 expireTime: 5 # 配置redis存储的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); /** * 发送短信 * * @param smsConfig 腾讯云短信配置对象 * @param templateParams 模板参数 * @param phoneNumbers 手机号数组 * @return SendStatus[],短信发送状态 */ public static SendStatus[] sendSms(SmsConfig smsConfig, String[] templateParams, String[] phoneNumbers) { try { // 实例化一个认证对象,入参需要传入腾讯云账户密钥对secretId,secretKey。 Credential cred = new Credential(smsConfig.getSecretId(), smsConfig.getSecretKey()); // 实例化一个http选项,可选,没有特殊需求可以跳过 HttpProfile httpProfile = new HttpProfile(); // SDK默认使用POST方法 httpProfile.setReqMethod("POST"); // SDK有默认的超时时间,非必要请不要进行调整 httpProfile.setConnTimeout(60); // 非必要步骤:实例化一个客户端配置对象,可以指定超时时间等配置 ClientProfile clientProfile = new ClientProfile(); // SDK默认用TC3-HMAC-SHA256进行签名,非必要请不要修改这个字段 clientProfile.setSignMethod("HmacSHA256"); clientProfile.setHttpProfile(httpProfile); // 实例化要请求产品(以sms为例)的client对象,第二个参数是地域信息,可以直接填写字符串ap-guangzhou,或者引用预设的常量 SmsClient smsClient = new SmsClient(cred, "ap-guangzhou", clientProfile); // 实例化一个请求对象 SendSmsRequest req = new SendSmsRequest(); // 设置短信应用ID:短信SdkAppId在[短信控制台]添加应用后生成的实际SdkAppId req.setSmsSdkAppId(smsConfig.getAppId()); // 设置短信签名内容:使用UTF-8编码,必须填写已审核通过的签名,签名信息可登录[短信控制台]查看 req.setSignName(smsConfig.getSign()); // 设置国际/港澳台短信SenderId:国内短信填空,默认未开通 req.setSenderId(""); // 设置模板ID:必须填写已审核通过的模板ID。模板ID可登录[短信控制台]查看 req.setTemplateId(smsConfig.getTemplateId()); // 设置下发手机号码,采用E.164标准,+[国家或地区码][手机号] req.setPhoneNumberSet(phoneNumbers); // 设置模板参数:若无模板参数,则设置为空 req.setTemplateParamSet(templateParams); // 通过client对象调用SendSms方法发起请求。注意请求方法名与请求对象是对应的,返回的res是一个SendSmsResponse类的实例,与请求对象对应 SendSmsResponse res = smsClient.SendSms(req); // 控制台打印日志输出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 键的过期时间 */ 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; } /** * 获取键的过期时间 * * @param key 键 * @return 过期时间 */ public long getExpire(String key) { return redisTemplate.getExpire(key, TimeUnit.MINUTES); } /** * 删除指定键的缓存 * * @param key 键 */ public void delete(String key) { redisTemplate.delete(key); } /** * 创建缓存的键 * * @param prefix 前缀 * @param phoneNumber 手机号 * @return 键 */ public static String createCacheKey(String prefix, String phoneNumber) { return prefix + phoneNumber; } }
用于生成随机验证码的工具类:
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(); /** * 生成指定位数的随机数字字符串 * * @param length 字符串长度 * @return 随机数字字符串 */ 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 { /** * 发送短信验证码 * * @param phoneNumber 手机号 * @return */ String sendSmsCode(String phoneNumber); /** * 验证短信验证码 * * @param phoneNumber 手机号 * @param smsCode 短信验证码 * @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实现类 * @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) { // 下发手机号码,采用e.164标准,+[国家或地区码][手机号] String[] phoneNumbers = {"+86" + phoneNumber}; // 生成6位随机数字字符串 String smsCode = RandomUtil.randomNumbers(6); // 模板参数:若无模板参数,则设置为空(参数1为随机验证码,参数2为有效时间) String[] templateParams = {smsCode, smsConfig.getExpireTime()}; // 发送短信验证码 SendStatus[] sendStatuses = SmsUtil.sendSms(smsConfig, templateParams, phoneNumbers); if ("Ok".equals(sendStatuses[0].getCode())) { // 创建缓存的key String key = RedisUtil.createCacheKey(smsConfig.getPhonePrefix(), phoneNumber); // 将验证码缓存到redis并设置过期时间 redisUtil.setCacheObject(key, smsCode, Long.parseLong(smsConfig.getExpireTime())); return "验证码发送成功"; } else { return "验证码发送失败:" + sendStatuses[0].getMessage(); } } @Override public String verifySmsCode(String phoneNumber, String smsCode) { // 创建key String key = RedisUtil.createCacheKey(smsConfig.getPhonePrefix(), phoneNumber); // 判断指定key是否存在并且未过期 if (redisUtil.hasKey(key)) { // 验证输入的验证码是否正确 if (smsCode.equals(redisUtil.getCacheObject(key))) { // 验证成功后删除验证码缓存 redisUtil.delete(key); return "验证成功"; } else { return "验证码错误"; } } else { return "验证码已失效"; } } }
四、腾讯云短信测试
运行项目,使用Postman进行接口测试。
1.发送短信验证码
发送如下POST请求以请求腾讯云短信服务向指定手机号发送短信验证码,请求地址为http://localhost:8080/sms/send
:
Redis客户端查看发现验证码已经存到了Redis中:
手机上也成功收到了验证码,说明发送短信成功:
2.验证短信验证码
发送如下POST请求验证短信验证码,请求地址为http://localhost:8080/sms/verify
,这里将验证码参数smscode
设为不同于之前发送到手机的验证码来模拟验证码输入错误:
再次发送如下POST请求验证短信验证码,这里输入正确的验证码:
Redis客户端刷新发现验证码缓存已经删除:
再次发送如下POST请求验证短信验证码,发现验证码已失效,满足短信验证码验证成功后就失效的业务需求:
代码示例
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
到此这篇关于SpringBoot2.x 集成腾讯云短信的文章就介绍到这了,更多相关SpringBoot腾讯云短信内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!