龙空技术网

Sprintboot+Redis集成腾讯云sms短信验证码服务

肚脐眼女孩 368

前言:

而今我们对“腾讯云sms服务”大概比较珍视,看官们都想要了解一些“腾讯云sms服务”的相关文章。那么小编在网上网罗了一些有关“腾讯云sms服务””的相关文章,希望看官们能喜欢,朋友们快快来学习一下吧!

签名申请

在开始之前,需要事前申请签名和创建短信模板,后面代码中会用到,地址在这里 腾讯云->申请签名,申请完等待审核通过即可。

申请签名创建模板等待审核代码实现pom.xml

首先要安装下腾讯云的包

<dependency>    <groupId>com.tencentcloudapi</groupId>    <artifactId>tencentcloud-sdk-java</artifactId>    <version>3.1.62</version>    <scope>compile</scope></dependency>
application.yml

把代码中需要用到的一些重要信息配置到yml中。

sms:  # 腾讯云中的secretId  secretId: AKIDb3MYaQ2QjvwmOnyukOq24vc7EWSCVcXE  # 腾讯云中的secretKey  secretKey: VpzyjIjy0oNjv0ywthXCiDXcVEkJewer  # 腾讯云中的appid,在应用列表那里  appid: 1400795322  # 签名管理中的前面内容  signName: 若依页面分层工具网  # 使用的端点,腾讯云默认sms.tencentcloudapi.com  endpoint: sms.tencentcloudapi.com  # 地区  region: ap-shanghai
sms配置类

通过@ConfigurationProperties读取配置信息并与 bean 绑定,在后面的业务代码中会读取这些配置信息。

@Data@Component@ConfigurationProperties(prefix = "sms")public class MessageConfig {    /** 腾讯云中的secretId */    private String secretId;    /** 腾讯云中的secretKey */    private String secretKey;    /** 腾讯云中的appid */    private String appid;    /** 签名 */    private String signName;    /** 使用的端点 */    private String endpoint;    /** 地区 */    private String region;}
sms短信实现

使用redis缓存每次发送的验证码,过期时间为10mins;表单提交时,做验证码的有效性和正确性判断。

下面这些代码是获取验证码的主要部分,后面在实现类中直接调该方法即可!

@Servicepublic class Message {    @Autowired    private MessageConfig messageConfig;    @Autowired    private RedisCache redisCache;    public SendSmsResponse smsClient(String phone) throws TencentCloudSDKException {        /* 必要步骤:         * 实例化一个认证对象,入参需要传入腾讯云账户密钥对secretId,secretKey。         * 这里采用的是从环境变量读取的方式,需要在环境变量中先设置这两个值。         * 你也可以直接在代码中写死密钥对,但是小心不要将代码复制、上传或者分享给他人,         * 以免泄露密钥对危及你的财产安全。         * CAM密匙查询: */        Credential cred = new Credential(messageConfig.getSecretId(), messageConfig.getSecretKey());        // 实例化一个http选项,可选,没有特殊需求可以跳过        HttpProfile httpProfile = new HttpProfile();        /* SDK默认使用POST方法。         * 如果你一定要使用GET方法,可以在这里设置。GET方法无法处理一些较大的请求 */        httpProfile.setReqMethod("POST");        /* SDK有默认的超时时间,非必要请不要进行调整         * 如有需要请在代码中查阅以获取最新的默认值 */        httpProfile.setConnTimeout(60);        /* SDK会自动指定域名。通常是不需要特地指定域名的,但是如果你访问的是金融区的服务         * 则必须手动指定域名,例如sms的上海金融区域名: sms.ap-shanghai-fsi.tencentcloudapi.com */        httpProfile.setEndpoint(messageConfig.getEndpoint());        /* 非必要步骤:         * 实例化一个客户端配置对象,可以指定超时时间等配置 */        ClientProfile clientProfile = new ClientProfile();        /* SDK 默认用 TC3-HMAC-SHA256 进行签名         * 非必要请不要修改该字段 */        clientProfile.setSignMethod("HmacSHA256");        clientProfile.setHttpProfile(httpProfile);        SmsClient client = new SmsClient(cred, messageConfig.getRegion(), clientProfile);        /* 实例化一个请求对象,根据调用的接口和实际情况,可以进一步设置请求参数         * 你可以直接查询SDK源码确定接口有哪些属性可以设置         * 属性可能是基本类型,也可能引用了另一个数据结构         * 推荐使用IDE进行开发,可以方便的跳转查阅各个接口和数据结构的文档说明 */        SendSmsRequest req = new SendSmsRequest();        /* 填充请求参数,这里request对象的成员变量即对应接口的入参         * 你可以通过官网接口文档或跳转到request对象的定义处查看请求参数的定义         * 基本类型的设置:         * 帮助链接:         * 短信控制台:          * sms helper:  */        /* 短信应用ID: 短信SdkAppId在 [短信控制台] 添加应用后生成的实际SdkAppId,示例如1400006666 */        String sdkAppId = messageConfig.getAppid();        req.setSmsSdkAppid(sdkAppId);        /* 短信签名内容: 使用 UTF-8 编码,必须填写已审核通过的签名,签名信息可登录 [短信控制台] 查看 */        String signName = messageConfig.getSignName();        req.setSign(signName);        /* 国际/港澳台短信 SenderId: 国内短信填空,默认未开通,如需开通请联系 [sms helper] */        String senderid = "";        req.setSenderId(senderid);        /* 用户的 session 内容: 可以携带用户侧 ID 等上下文信息,server 会原样返回 */        String sessionContext = SecurityUtils.getUsername();        req.setSessionContext(sessionContext);        /* 短信号码扩展号: 默认未开通,如需开通请联系 [sms helper] */        String extendCode = "";        req.setExtendCode(extendCode);        /* 模板 ID: 必须填写已审核通过的模板 ID。模板ID可登录 [短信控制台] 查看 */        String templateId = "1771557";        req.setTemplateID(templateId);        /* 下发手机号码,采用 E.164 标准,+[国家或地区码][手机号]         * 示例如:+8613711112222, 其中前面有一个+号 ,86为国家码,13711112222为手机号,最多不要超过200个手机号 */        String[] phoneNumberSet = {"+86" + phone};        req.setPhoneNumberSet(phoneNumberSet);        /* 模板参数: 若无模板参数,则设置为空 */        int code = (int) ((Math.random() * 9 + 1) * 1000); //生成6位随机数字        String[] templateParamSet = {String.valueOf(code)};        req.setTemplateParamSet(templateParamSet);        // redis缓存手机号对应的验证码,这里的code要存入字符串类型,要和前端传入的code类型保持一致!        redisCache.setCacheObject(Constants.SMS_CODE_KEY + phone, String.valueOf(code), Constants.SMS_EXPIRATION, TimeUnit.MINUTES);        /* 通过 client 对象调用 SendSms 方法发起请求。注意请求方法名与请求对象是对应的         * 返回的 res 是一个 SendSmsResponse 类的实例,与请求对象对应 */        SendSmsResponse res = client.SendSms(req);        return res;    }}

Constants类

    public class Constants{        /**         * sms有效期         */        public static final Integer SMS_EXPIRATION = 10;        /**         * 短信sms redis key         */        public static final String SMS_CODE_KEY = "sms_codes:";    }
redis配置添加依赖
<!-- redis 缓存操作 --><dependency>    <groupId>org.springframework.boot</groupId>    <artifactId>spring-boot-starter-data-redis</artifactId></dependency>
修改配置
# Spring配置spring:  redis:    # 地址    host: xxx.xx.xx.xx    # 端口,默认为6379    port: 6379    # 数据库索引    database: 0    # 密码    password: root    # 连接超时时间    timeout: 10s    lettuce:      pool:        # 连接池中的最小空闲连接        min-idle: 0        # 连接池中的最大空闲连接        max-idle: 8        # 连接池的最大数据库连接数        max-active: 8        # #连接池最大阻塞等待时间(使用负值表示没有限制)        max-wait: -1ms
序列化和反序列化redis的key值

序列化

狭义的层面:将对象转换为字节广义的层面:将对象转换为指定格式的字符串

反序列化

狭义的层面:将字节转换为对象广义的层面:将指定格式的字符串转换为对象

序列化和反序列化代码

 /** * redis配置 */@Configuration@EnableCachingpublic class RedisConfig extends CachingConfigurerSupport{    @Bean    @SuppressWarnings(value = { "unchecked", "rawtypes" })    public RedisTemplate redisTemplate(RedisConnectionFactory connectionFactory)    {        RedisTemplate template = new RedisTemplate<>();        template.setConnectionFactory(connectionFactory);    FastJson2JsonRedisSerializer serializer = new FastJson2JsonRedisSerializer(Object.class);    ObjectMapper mapper = new ObjectMapper();    mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);    mapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);    serializer.setObjectMapper(mapper);    // 使用StringRedisSerializer来序列化和反序列化redis的key值    template.setKeySerializer(new StringRedisSerializer());    template.setValueSerializer(serializer);    // Hash的key也采用StringRedisSerializer的序列化方式    template.setHashKeySerializer(new StringRedisSerializer());    template.setHashValueSerializer(serializer);    template.afterPropertiesSet();    return template;}@Beanpublic DefaultRedisScript<Long> limitScript(){    DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();    redisScript.setScriptText(limitScriptText());    redisScript.setResultType(Long.class);    return redisScript;}/** * 限流脚本 */private String limitScriptText(){    return "local key = KEYS[1]\n" +            "local count = tonumber(ARGV[1])\n" +            "local time = tonumber(ARGV[2])\n" +            "local current = redis.call('get', key);\n" +            "if current and tonumber(current) > count then\n" +            "    return tonumber(current);\n" +            "end\n" +            "current = redis.call('incr', key)\n" +            "if tonumber(current) == 1 then\n" +            "    redis.call('expire', key, time)\n" +            "end\n" +            "return tonumber(current);";}}复制代码}
增加工具类

redis工具类

 @Componentpublic class RedisCache{    @Autowired    public RedisTemplate redisTemplate;/** * 缓存基本的对象,Integer、String、实体类等 * * @param key 缓存的键值 * @param value 缓存的值 */public <T> void setCacheObject(final String key, final T value){    redisTemplate.opsForValue().set(key, value);}/** * 缓存基本的对象,Integer、String、实体类等 * * @param key 缓存的键值 * @param value 缓存的值 * @param timeout 时间 * @param timeUnit 时间颗粒度 */public <T> void setCacheObject(final String key, final T value, final Integer timeout, final TimeUnit timeUnit){    redisTemplate.opsForValue().set(key, value, timeout, timeUnit);}/** * 设置有效时间 * * @param key Redis键 * @param timeout 超时时间 * @return true=设置成功;false=设置失败 */public boolean expire(final String key, final long timeout){    return expire(key, timeout, TimeUnit.SECONDS);}/** * 设置有效时间 * * @param key Redis键 * @param timeout 超时时间 * @param unit 时间单位 * @return true=设置成功;false=设置失败 */public boolean expire(final String key, final long timeout, final TimeUnit unit){    return redisTemplate.expire(key, timeout, unit);}/** * 获得缓存的基本对象。 * * @param key 缓存键值 * @return 缓存键值对应的数据 */public <T> T getCacheObject(final String key){    ValueOperations<String, T> operation = redisTemplate.opsForValue();    return operation.get(key);}/** * 删除单个对象 * * @param key */public boolean deleteObject(final String key){    return redisTemplate.delete(key);}/** * 删除集合对象 * * @param collection 多个对象 * @return */public long deleteObject(final Collection collection){    return redisTemplate.delete(collection);}/** * 缓存List数据 * * @param key 缓存的键值 * @param dataList 待缓存的List数据 * @return 缓存的对象 */public <T> long setCacheList(final String key, final List<T> dataList){    Long count = redisTemplate.opsForList().rightPushAll(key, dataList);    return count == null ? 0 : count;}/** * 获得缓存的list对象 * * @param key 缓存的键值 * @return 缓存键值对应的数据 */public <T> List<T> getCacheList(final String key){    return redisTemplate.opsForList().range(key, 0, -1);}/** * 缓存Set * * @param key 缓存键值 * @param dataSet 缓存的数据 * @return 缓存数据的对象 */public <T> BoundSetOperations<String, T> setCacheSet(final String key, final Set<T> dataSet){    BoundSetOperations<String, T> setOperation = redisTemplate.boundSetOps(key);    Iterator<T> it = dataSet.iterator();    while (it.hasNext())    {        setOperation.add(it.next());    }    return setOperation;}/** * 获得缓存的set * * @param key * @return */public <T> Set<T> getCacheSet(final String key){    return redisTemplate.opsForSet().members(key);}/** * 缓存Map * * @param key * @param dataMap */public <T> void setCacheMap(final String key, final Map<String, T> dataMap){    if (dataMap != null) {        redisTemplate.opsForHash().putAll(key, dataMap);    }}/** * 获得缓存的Map * * @param key * @return */public <T> Map<String, T> getCacheMap(final String key){    return redisTemplate.opsForHash().entries(key);}/** * 往Hash中存入数据 * * @param key Redis键 * @param hKey Hash键 * @param value 值 */public <T> void setCacheMapValue(final String key, final String hKey, final T value){    redisTemplate.opsForHash().put(key, hKey, value);}/** * 获取Hash中的数据 * * @param key Redis键 * @param hKey Hash键 * @return Hash中的对象 */public <T> T getCacheMapValue(final String key, final String hKey){    HashOperations<String, String, T> opsForHash = redisTemplate.opsForHash();    return opsForHash.get(key, hKey);}/** * 删除Hash中的数据 *  * @param key * @param mapkey */public void delCacheMapValue(final String key, final String hkey){    HashOperations hashOperations = redisTemplate.opsForHash();    hashOperations.delete(key, hkey);}/** * 获取多个Hash中的数据 * * @param key Redis键 * @param hKeys Hash键集合 * @return Hash对象集合 */public <T> List<T> getMultiCacheMapValue(final String key, final Collection<Object> hKeys){    return redisTemplate.opsForHash().multiGet(key, hKeys);}/** * 获得缓存的基本对象列表 * * @param pattern 字符串前缀 * @return 对象列表 */public Collection<String> keys(final String pattern){    return redisTemplate.keys(pattern);}}复制代码}
接口Controller

get请求,传递一个手机号码即可。

@RestController@RequestMapping("/msg")public class SmsMessage {    @Autowired    private ISmsService ISmsService;    //发送短信    @GetMapping("/sendMsg")    public SendSmsResponse sendMessage(String phone) throws TencentCloudSDKException {        return ISmsService.sendLoginVeryCodeMessage(phone);    }}
Service
public interface ISmsService {    public SendSmsResponse sendLoginVeryCodeMessage(String phone) throws TencentCloudSDKException;    }
impl

只需要在实现类中调用封装好的sms业务代码即可!

@Servicepublic class SmsServiceImpl implements ISmsService {    @Autowired    private Message message;    public SendSmsResponse sendLoginVeryCodeMessage(String phone) throws TencentCloudSDKException {        return message.smsClient(phone);    }}
验证码校验

此时此刻,已经通过前端页面请求获取到验证码,并且填写至表单中,提交的时候需要对当前的提交的验证码进行校验。之前发送验证码的时候,已经把验证码的内容保存到了redis中;此时只需要把redis缓存中的数据取出来,提交信息中的电话号码为redis的key,根据这个key获取对应的验证码,比对和提交的验证码是否一致即可!

@PostMappingpublic AjaxResult add(@RequestBody MerApply merApply) {    // 获取提交的手机号码    String smsCode = Constants.SMS_CODE_KEY + merApply.getPhone();    // 根据手机号码获取redis缓存中的value    String code = redisCache.getCacheObject(smsCode);    if (code == null) {        return error("验证码已失效");    }    if (!merApply.getCode().equals(code)) {        return error("验证码校验不通过");    }    merApply.setCreateBy(getUsername());    return toAjax(this.merApplyService.insert(merApply));}
案例

去往案例地址>>>

作者:Lakeiedward

链接:

标签: #腾讯云sms服务