前言

很久之前的一篇文章, 最新web/java/jsp实现发送手机短信验证码和邮箱验证码的注册登录功能(详细),截止到目前,依然有很多小伙伴,私信需要帮助,于是我再加一篇,让大家能更好的使用。(当然,两篇文章,依然都是有效的,就看你使用的是什么开发环境了)

鉴于第一篇文章是用的一个第三方平台,很多小伙伴没有用过,所以本次实现短信验证码服务,挑选了国内两大运营商(阿里云、腾讯云),来实现手机短信功能。

注意点:

  • 1.代码是最新版本的,很多博客写的都是低版本的,有些参数名称都变了。
  • 2.微服务短信业务实战,需要有一定的SpringBoot基础。
  • 3.结合Redis缓存,打造成一个可移植的服务,这样,下次就直接搬运代码即可。

好了,废话不多说,开始今天我们的学习,

一、基于阿里云的短信服务v3.0

内容如下:

1、阿里云子账号开通权限,(获取AccessKey)
2、开通短信服务,(白嫖免费套餐包)
3、创建短信签名,(长得帅能通过)
4、创建短信模板,(要说啥全靠它)
5、SDK工具包,本地测试,(采用最新3.0版本)
6、集成Redis实战,打造真实业务场景下通用型短信微服务,(工作搬运)

当然了,正好赶上节日了,这不,文中有活动地点,可以有免费短信,正好白嫖一波~

1.1、阿里云子账号权限开通

这个不用说了把,打开 阿里云官网,登录,没账号就注册(不会真的有人没有吧?)

1.开启子用户

登录进去之后,点击AccessKey管理

其实就是去开通一个权限账户,便于操作,可以是当前用户,也可以是新建一个子用户。

使用子用户的目的在于安全,当然你完全可以不使用。(我就是~),图片选这个是我建议。

2.新建用户组

用户组,就是建了个群。

3.设置权限

就给群,设置一下,它该拥有哪些权限?比如我们这次就赋予它操作短信的权限。

4.新增一个用户
有了群之后,就需要拉个人进来。因此需要创建一个具体用来操作的子账号。


值得注意的是,必须开启编程访问,这样才可以API的访问和测试了。

最后把这个人拉进群,这样这个人就拥有了短信操作权限了。

5.得到key的id和密码。

保存这个的用户的授权码,自己拿该小本本记录一下,保存好,泄露之后请删除或禁用。

1.2、开通短信服务

1.开通短信服务

当然了,正好活动,要你们个白嫖地址:618阿里云短信免费白嫖包

图示:

注意点:套餐包过期以后,短信是67个字免费,一天10条短信。反正目前可以白嫖2月了~

2.新增签名

它是你的短信开通的单位标识。

(其实,你可以随便写个公司名称,然后写个正当理由,等待审核,好多小傻瓜苦苦喊~)。

记下申请通过的签名名称。

对于个人申请签名,请注意:

  • 个人客户申请使用各类产品名/店铺名等作为短信签名的,需提供相应的企业授权和资质证明(如授权企业的营业执照、商标、备案信息等)。
  • 个人客户签名用途说为工信部备案的网站全称或简称、APP应用的全称或简称、公众号或小程序的名称全称或简称,并上传相应用途的审核材料。
  • 个人客户不可申请不具实际意义的中性签名,如客服通知、客户您好等。
  • 个人用户限申请1个验证码签名,申请已达上限,若已删除旧签名,请重新登陆阿里云账号申请。

3.新增模板

模板内容,就是你要发送出去的短信内容。

注意事项

  • ${xxxx}代表参数 可以使用 java进行传入
  • 申请说明不能随便写! 否则不能通过审核!!

申请通过之后,记下申请通过的模板code,它对应的变量是:

其实,签名和模板的效果图,如下:

两个都申请之后 ,大概10分钟左右就审核完成了 ,最多超不过两小时。

1.3、SDKv3.0工具包本地测试

1.新建一个SpringBoot项目,如下:

2…添加Maven依赖,阿里云Java SDK的开发工具包。

阿里云目前,SDK工具包分为2个类,一个aliyun-java-sdk-core包,另外一个是com.aliyun-dysms-api包。

注意:目前很多博客的教程,都是采用旧版本的依赖和写法,如下:

//旧版本
<dependency><groupId>com.aliyun</groupId><artifactId>aliyun-java-sdk-core</artifactId><version>4.5.13</version>
</dependency>

然而,阿里云SDK 已经发生了变化,当然旧版的短信接口99%是用不了的。

目前阿里云的短信版本:

所以本次,我们采用用的阿里云最新给出的 V3.0版本,依赖如下:

//新版本
<dependency><groupId>com.aliyun</groupId><artifactId>dysmsapi20170525</artifactId><version>2.0.1</version>
</dependency>
<dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.76</version>
</dependency>

附带这把fastjson都引入,因为SDK要求参数是json格式的。

3.测试短信发送

在项目的 test 路径下,新建一个SmsProjectApplicationTests类,进行SDK调用测试。

import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import com.aliyun.dysmsapi20170525.Client;
import com.aliyun.dysmsapi20170525.models.*;
import com.aliyun.teautil.Common;
import net.minidev.json.JSONObject;
import java.util.HashMap;@SpringBootTest
class SmsProjectApplicationTests {@Testvoid contextLoads() throws Exception {Config config = new Config();config.accessKeyId = "你的id";config.accessKeySecret = "你的id的key";config.endpoint = "dysmsapi.aliyuncs.com";Client client = new Client(config);// 1.发送短信-构建参数SendSmsRequest sendSmsRequest = new SendSmsRequest().setPhoneNumbers("手机号码").setSignName("申请的签名名称").setTemplateCode("申请的模版CODE");//2.构建短信验证码-json格式HashMap<String, Object> map = new HashMap<>();map.put("code",9527);sendSmsRequest.setTemplateParam(JSONObject.toJSONString(map));//3.响应responseSendSmsResponse sendResp = client.sendSms(sendSmsRequest);String code = sendResp.body.code;if (!Common.equalString(code, "OK")) {System.out.println(("错误信息: " + sendResp.body.message + ""));return ;}System.out.println(sendResp.body.message);// 4. 等待 10 秒后查询结果-可去除Common.sleep(10000);// 5.查看短信发送记录和发送状态-可去除QuerySendDetailsRequest querySendDetailsRequest = new QuerySendDetailsRequest().setPhoneNumber("手机号码").setSendDate("20210602")  //最近30天,格式为yyyyMMdd.setCurrentPage(1L)         //当前页码.setPageSize(10L);          //每页显示记录数,范围为1~50。//返回值是回执id即BizId字段:"RequestId": "08BE118C-32B2-4248-8884-535D40A4AA63",QuerySendDetailsResponse queryResp = client.querySendDetails(querySendDetailsRequest);// 打印结果List<QuerySendDetailsResponseBody.QuerySendDetailsResponseBodySmsSendDetailDTOsSmsSendDetailDTO> dtos = queryResp.body.smsSendDetailDTOs.smsSendDetailDTO;for (QuerySendDetailsResponseBody.QuerySendDetailsResponseBodySmsSendDetailDTOsSmsSendDetailDTO dto : dtos) {if (com.aliyun.teautil.Common.equalString("" + dto.sendStatus + "", "3")) {System.out.println(("" + dto.phoneNum + " 发送成功,接收时间: " + dto.receiveDate + ""));} else if (com.aliyun.teautil.Common.equalString("" + dto.sendStatus + "", "2")) {System.out.println(("" + dto.phoneNum + " 发送失败"));} else {System.out.println(("" + dto.phoneNum + " 正在发送中..."));}}}
}

上文中验证码模板的参数格式是{“code”:“9527”},尽管9527是数字,也要按照字符串传递,要求是JSON格式。

运行一下以Web项目,可以看见短信发送成功。

(其实直接在类的main方法里就可以运行,在Test类中运行完全是为了模拟一下web环境下)

发送结果:

测试方法通过之后,我们就可以把这些代码,琐碎的提出,打造成为工具类,在结合真实业务场景,做出一个通用的短信验证码微服务。

1.4、集成Redis,打造通用型短信微服务

在真实的业务场景中,通常我们使用短信,是用在通知类、验证码、营销类。由于客户不是及时性的,那么这些接口往往需要设置一定的有效时间,为了防止重复发送短信造成客户烦躁,我们通常会结合Redis缓存,来做短信通知。

so,今天结合Redis缓存,打造可以移植的,可对外提供服务的接口,这就得到一个通用型短信微服务,以后项目需要它,直接搬运即可。

1.引入redis依赖

当然,为了更接近于真实的业务常见,我们需要引入一下Json和Redis的依赖。

<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>
</dependency>

json上面已经引入,web是自带的,没有就加上。

2.修改配置类:

在application.yml中增加短信配置属性

server:port: 8080#redis配置
spring:redis:database: 4host: 127.0.0.1port: 6379password:#redis客户端连接的超时时间(单位毫秒)timeout: 5000#阿里云短信配置
aliyun:sms:accessKeyId: Lxxxxxxyp # 你的accessKeyIDaccessKeySecret: bxxxxxxxxxiWO # 你的accessKeySecretsignName: '涛xxxx屋'                  # 你的签名名称templateCode: 'SMS_20xxxxxx0'          # 你的模板CODE

3.新建工具类:

既然要封装成一个通用的微服务,就得把一些连接,配置抽象为工具类来使用。

SampleByAliUtil类:

package com.zoutao.alisms.utils;import com.aliyun.dysmsapi20170525.Client;
import com.aliyun.dysmsapi20170525.models.*;
import com.aliyun.teaopenapi.models.*;
import java.util.List;/*** TODO 阿里云短信服务V3.0** @author zoutao.blog.csdn.net* @date 2021/6/2*/
public class SampleByAliUtil {/*** 使用AK&SK构建短信客户端Client** @param accessKeyId* @param accessKeySecret* @return Client* @throws Exception*/public static Client createClient(String accessKeyId, String accessKeySecret) throws Exception {Config config = new Config();config.accessKeyId = accessKeyId;config.accessKeySecret = accessKeySecret;// 访问的域名config.endpoint = "dysmsapi.aliyuncs.com";return new Client(config);}/*** 查询发送结果,正常返回示例:* {*     "TotalCount": 1,*     "Message": "OK",*     "RequestId": "819BE656-D2E0-4858-8B21-B2E477085AAF",*     "Code": "OK",*     "SmsSendDetailDTOs": {*         "SmsSendDetailDTO": {*             "TemplateCode": "SMS_122310183",*             "ReceiveDate": "2019-01-08 16:44:13",*             "PhoneNum": 15200000000,*             "Content": "【阿里云】验证码为:123,您正在登录,若非本人操作,请勿泄露",*             "SendStatus": 3,*             "OutId": 123,*             "SendDate": "2021-01-08 16:44:10",*             "ErrCode": "DELIVERED"*         }*     }* }*/public static void querySmsSendStatus(String accessKeyId, String accessKeySecret,String phoneNumber,String sendDate) throws Exception {if (phoneNumber.isEmpty()||sendDate.isEmpty()){System.out.printf("参数为空错误!");}Client client = SampleByAliUtil.createClient(accessKeyId, accessKeySecret);QuerySendDetailsRequest querySendDetailsRequest = new QuerySendDetailsRequest().setPhoneNumber(phoneNumber).setSendDate(sendDate)  //日期格式yyyyMMdd.setPageSize(10L).setCurrentPage(1L);QuerySendDetailsResponse response = client.querySendDetails(querySendDetailsRequest);System.out.println("请求ID:"+response.body.getRequestId());System.out.println("状态码:"+response.body.getCode());System.out.println("描述:"+response.body.getMessage());System.out.println("发送总条数:"+response.body.getTotalCount());//解析发送明细List<QuerySendDetailsResponseBody.QuerySendDetailsResponseBodySmsSendDetailDTOsSmsSendDetailDTO> dtos = response.body.smsSendDetailDTOs.smsSendDetailDTO;for (QuerySendDetailsResponseBody.QuerySendDetailsResponseBodySmsSendDetailDTOsSmsSendDetailDTO dto : dtos) {System.out.println("发送明细:"+dto.toMap());}}public static void main(String[] args) throws Exception {String accessKeyId="xxxxx"; String accessKeySecret="xxxxxxx";String phoneNumber = "182xxxxxxxx";String sendDate = "20210602";  //日期格式yyyyMMddquerySmsSendStatus(accessKeyId,accessKeySecret,phoneNumber,sendDate);}
}

4.新建service:

package com.zoutao.alisms.service;public interface  SendSmsService {/*** 发送短信验证码的接口* by 阿里云* @param phoneNum 手机号* @param code     验证码*/boolean sendSmsByAli(String phoneNum, String code) throws Exception;
}

5.新建serviceImpl:

package com.zoutao.alisms.service.impl;import com.alibaba.fastjson.JSONObject;
import com.aliyun.dysmsapi20170525.Client;
import com.aliyun.dysmsapi20170525.models.SendSmsRequest;
import com.aliyun.dysmsapi20170525.models.SendSmsResponse;
import com.aliyun.teautil.Common;
import com.zoutao.alisms.service.SendSmsService;
import com.zoutao.alisms.utils.SampleByTxUtil;
import com.zoutao.alisms.utils.SampleByAliUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.rmi.ServerException;
import java.util.HashMap;@Service
public class SendSmsServiceImpl implements SendSmsService {private static final Logger log = LoggerFactory.getLogger(SendSmsServiceImpl.class);@Value("${aliyun.sms.accessKeyId}")private String accessKeyId;@Value("${aliyun.sms.accessKeySecret}")private String accessKeySecret;@Value("${aliyun.sms.signName}")private String signName;@Value("${aliyun.sms.templateCode}")private String templateCode;/*** 阿里云发送短信接口*/@Overridepublic boolean sendSmsByAli(String phoneNum, String code) throws Exception {Client client = SampleByAliUtil.createClient(accessKeyId, accessKeySecret);SendSmsRequest sendSmsRequest = new SendSmsRequest().setSignName(signName).setTemplateCode(templateCode);sendSmsRequest.setPhoneNumbers(phoneNum);//2.构建短信验证码-要求json格式,code是配置在阿里云中的变量名,要求一致HashMap<String, Object> map = new HashMap<>();map.put("code",code);sendSmsRequest.setTemplateParam(JSONObject.toJSONString(map));try {SendSmsResponse sendResp = client.sendSms(sendSmsRequest);String codeResp = sendResp.body.code;if (!Common.equalString(codeResp, "OK")) {log.error("错误信息: " + sendResp + "");return false;}return true;} catch (ServerException | ClassCastException e) {e.printStackTrace();}return false;}
}

6.新建controller:

package com.zoutao.alisms.controller;import com.aliyun.tea.utils.StringUtils;
import com.zoutao.alisms.service.SendSmsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.web.bind.annotation.*;
import java.util.concurrent.TimeUnit;/*** 短信服务控制* * @author zoutao* @date 2021/6/1*/
@RestController
@CrossOrigin //跨域支持
public class SendSmsController {@Autowiredprivate SendSmsService sendSmsService;@Autowiredprivate RedisTemplate<String,String> redisTemplate;//阿里云发送短信@GetMapping("/sendSms")public String sendSms(@RequestParam("phoneNum") String phoneNum) throws Exception {// 获取redis操作String的对象ValueOperations<String, String> opsForValue = redisTemplate.opsForValue();// 根据手机号进行查询缓存String phone = opsForValue.get(phoneNum);// 如果手机号在redis中不存在的话才进行发送验证码操作if (StringUtils.isEmpty(phone)) {// 生成4位随机数String code  = String.valueOf(Math.random()).substring(3, 7);// 调用业务层接口 发送验证码boolean sendSmsFlag = sendSmsService.sendSmsByAli(phoneNum, code);if (sendSmsFlag) {// 发送成功之后往redis中存入该手机号以及验证码 并设置超时时间5分钟opsForValue.set(phoneNum, code, 5, TimeUnit.MINUTES);}return "发送验证码到:" + phoneNum + "成功! " + "Message:" + sendSmsFlag;} else {return "该手机号:" + phoneNum + "的验证码还没过期,剩余:" + redisTemplate.getExpire(phoneNum) + "秒后可再次进行发送!";}}//查询验证码@GetMapping("/checkCode/{key}/{code}")public String checkCode(@PathVariable("key") String number,@PathVariable("code") String code) {// 获取到操作String的对象ValueOperations<String, String> stringR = redisTemplate.opsForValue();// 根据Key进行查询String redisCode = stringR.get(number);if (code.equals(redisCode)) {return "成功";} else {return redisCode==null ? "请先获取验证码在进行校验!" : "错误";}}
}

其中,我们注入了Redis操作模板对象RedisTemplate,把电话号码和验证码,组成kv形式,存储到缓存中,设置有效时间,这样就可以避免重复的请求短信。

为了防止前后端分离时,前端情况出现问题,增加了@CrossOrigin 的跨域支持。

7.web环境测试短信接口

启动redis-service服务,启动项目,调用短信发送的get请求:
http://localhost:8080/sendSms?phoneNum=xxxxxx

接口反馈,如图:

手机短信:

此时的Redis里,存在了键值对,且有效时间:

此时再次调用短信发送的接口,就会出现验证码未过期的提示了:

注意事项:

  • 不要轻易把阿里云的 accessKey 和 accessSecret 泄露,它是代表你账户的所有权限。
  • 在阿里云的SMS控制台内,可以设置 短信的安全预警、发送频率等设置,防止被别人盗刷。(毕竟收费的~)

除此之外,在querySmsSendStatus方法,是用来获取之前发送过短信的记录,按照电话和时间为参数,直接传参调用即可。

调用querySmsSendStatus方法:

SampleByAliUtil.querySmsSendStatus(accessKeyId,accessKeySecret,phoneNumber,sendDate);

获取发送的结果:

测试成功!

在querySmsSendStatus()方法中,是调用的 QuerySendDetails接口,根据短信发送日期来查看发送记录和短信内容,当然也可以根据流水号查询指定日期指定请求的发送详情。如果指定日期短信发送量较大,可以分页查看。指定每页显示的短信详情数量和查看的页数,即可分页查看发送记录。

二、基于腾讯云的短信服务v3.0

当然,有的朋友习惯用腾讯云提供的服务,既然有了阿里云的短信经验之后,腾讯云的短信服务,就大同小异了。

内容如下:

1、腾讯云账号开通权限,(获取AccessKey)
2、接入短信能力,
3、创建短信签名,
4、创建短信模板,
5、SDK工具包,本地测试,(采用最新3.0版本)
6、集成Redis实战,打造通用型短信微服务,

具体过程图示:

2.1、腾讯云账号开通权限,(获取AccessKey)

点击查询链接:CAM密匙查询

或者是:

登录账号,然后在右上角个人头像下,点击访问管理,

进入api管理,新建一个密钥:

老规矩,记录下id和key,它是代表你的账号。

2.2、接入短信能力

产品列表中搜索短信,然后点击短信,进入开通短信服务界面。

首次创建签名和模版,会赠送100条国内短信。

2.3、创建短信签名

注意,有一定的要求。

填入的内容:

申请通过之后,记录一下签名的名称。

2.4、创建短信模板

计费需知:

  • 短信长度(签名+正文)不超过70字时,按照1条短信计费;超过70字即为长短信时,按67字/条分隔成多条计费。
  • 例如,短信长度为150字,则按照67字/67字/16字分隔成3条计费。

当然了,我们有免费条数,不担心这个问题。

申请填写如下:

根据你自身的条件进行填写。

模板示例:{1}为您的验证码,请于{2}分钟内填写。如非本人操作,请忽略本短信。
(其中{1}、{2}为可自定义的内容,须从1开始连续编号,如{1}、{2}等,他们就我们代码中要自定义的参数值)

短信签名和模板提交后,预计2小时完成审核。

2.5、获取应用id

在短信控制台的应用管理中,点击应用列表,无就创建,有就默认。

记录下这个创建好的SDKAppID。

2.6、SDK本地测试

可以通过 SDK 使用所有 短信 API。

注:由于腾讯云 API3.0 安全性有所提升,接口鉴权较为复杂,所以官方建议使用 SDK 来使用云短信服务。

前提条件:

  • 已开通短信服务,
  • 已在访问管理控制台 >【API密钥管理】页面获取 SecretID 和 SecretKey。
  • SecretID 用于标识 API 调用者的身份。
  • SecretKey 用于加密签名字符串和服务器端验证签名字符串的密钥,SecretKey 需妥善保管,避免泄露。
  • 短信的调用地址为sms.tencentcloudapi.com。

我们都已经满足,接下来可以用 生成SDK发送短信demo ,也可以直接自行编码测试,我们自行测试。

在上面的springboot项目中,从pom引入,2021.06最新的v3.0版。

依赖如下:

<!-- 腾讯云短信服务2021.6最新版 -->
<dependency><groupId>com.tencentcloudapi</groupId><artifactId>tencentcloud-sdk-java</artifactId><version>4.0.11</version>
</dependency>

测试SDK发送短信:

package com.zoutao.alisms.utils;import com.tencentcloudapi.common.Credential;
import com.tencentcloudapi.common.profile.ClientProfile;
import com.tencentcloudapi.common.profile.HttpProfile;
import com.tencentcloudapi.common.exception.TencentCloudSDKException;
import com.tencentcloudapi.sms.v20190711.SmsClient;
import com.tencentcloudapi.sms.v20190711.models.*;;/*** TODO 腾讯云短信发送测试** @author zoutao.blog.csdn.net* @date 2021/6/2*/
public class tengxyunTest {public static void main(String[] args) {try {Credential cred = new Credential("SecretId", "SecretKey");HttpProfile httpProfile = new HttpProfile();httpProfile.setEndpoint("sms.tencentcloudapi.com");ClientProfile clientProfile = new ClientProfile();clientProfile.setHttpProfile(httpProfile);SmsClient client = new SmsClient(cred, "ap-guangzhou", clientProfile);SendSmsRequest req = new SendSmsRequest();String[] phoneNumberSet1 = {"+861826666666"};req.setPhoneNumberSet(phoneNumberSet1);req.setSmsSdkAppid("14006666666");req.setSign("编程一点通公众号");req.setTemplateID("98666666");//模板参数{1}{2}值String[] templateParamSet1 = {"9527", "15"};req.setTemplateParamSet(templateParamSet1);SendSmsResponse resp = client.SendSms(req);System.out.println(SendSmsResponse.toJsonString(resp));} catch (TencentCloudSDKException e) {System.out.println(e.toString());}}
}

直接运行该main函数,正常情况就会发送短信通知了:


是不是感觉超简单?

2.7、集成Redis,打造通用型短信微服务

上面的springboot项目已经整合了阿里云实现短信服务,我们也将腾讯云的短信服务整合在里面,后期自行挑选即可。

配置类添加参数:

#腾讯云短信配置
tengxun:sms:secretId: AKID2uWsw666666666666666666IDrsecretKey: OVLvFI4Y888888888888888McbJlvosdkAppid: "14022222260"sign: "编程一点通公众号"templateId: "92222224"

新建工具类:

package com.zoutao.alisms.utils;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.v20190711.SmsClient;
import com.tencentcloudapi.sms.v20190711.models.*;/*** TODO 腾讯云短信服务V3.0** @author zoutao.blog.csdn.net* @date 2021/6/2*/
public class SampleByTxUtil {//短信发送public static void main(String[] args) {String SecretId = "测试";String SecretKey = "测试";//sendSmsByTxy(SecretId, SecretKey);//sendStatusStatistics(SecretId, SecretKey);//callbackStatusStatistics(SecretId, SecretKey);PullSmsSendStatus(SecretId, SecretKey);}/*** 使用AK&AKS创建短信客户端SmsClient** @param secretId* @param secretKey* @return SmsClient*/public static SmsClient createClient(String secretId, String secretKey) {Credential cred = new Credential(secretId, secretKey);// 实例化一个http选项,可选,没有特殊需求可以跳过HttpProfile httpProfile = new HttpProfile();// SDK会自动指定域名,可以不配置(服务商域名)httpProfile.setEndpoint("sms.tencentcloudapi.com");ClientProfile clientProfile = new ClientProfile();clientProfile.setHttpProfile(httpProfile);//Endpoint (地域信息),可以直接填写字符串ap-guangzhou,或引用预设的常量SmsClient client = new SmsClient(cred, "ap-guangzhou", clientProfile);return client;}/**发送短信,可以写在impl也可以抽离到工具类*/public static String sendSmsByTxy(String secretId, String secretKey){try{SmsClient client = SampleByTxUtil.createClient(secretId, secretKey);SendSmsRequest req = new SendSmsRequest();//下发手机号码,采用 E.164 标准,格式为+[国家或地区码][手机号],单次请求最多支持200个手机号String[] phoneNumberSet = {"+8618666666666"};req.setPhoneNumberSet(phoneNumberSet);//申请的材料req.setSmsSdkAppid("140666660");req.setSign("空空代码网");req.setTemplateID("986666");//模板参数String[] templateParamSet1 = {"9527", "15"};req.setTemplateParamSet(templateParamSet1);//发送短信请求SendSmsResponse resp = client.SendSms(req);System.out.println(SendSmsResponse.toJsonString(resp));return SendSmsResponse.toJsonString(resp);} catch (TencentCloudSDKException e) {System.out.println(e.toString());}return null;}/**拉取回执状态*/public static void PullSmsSendStatus(String SecretId,String SecretKey){try{SmsClient client = SampleByTxUtil.createClient(SecretId, SecretKey);PullSmsSendStatusRequest req = new PullSmsSendStatusRequest();req.setLimit(10L);  // 设置拉取最大条数,最多100条req.setSmsSdkAppid("14006666");PullSmsSendStatusResponse resp = client.PullSmsSendStatus(req);// 输出json格式的字符串回包System.out.println(PullSmsSendStatusResponse.toJsonString(resp));} catch (TencentCloudSDKException e) {System.out.println(e.toString());}}/*** 发送短信的数量统计* SendStatusStatistics:{FeeCount:计费条数统计,RequestCount:提交量统计,RequestSuccessCount:提交成功量统计}* RequestId:唯一请求 ID,* {*   "Response": {*     "SendStatusStatistics": {*       "FeeCount": 0,*       "RequestCount": 1,*       "RequestSuccessCount": 0*     },*     "RequestId": "9e6981c8-db52-4720-abb9-ae14decfa25d"*   }* }* @author zoutao* @version v2.0* @date 2021/6/2*/public static void sendStatusStatistics(String SecretId,String SecretKey){try{SmsClient client = SampleByTxUtil.createClient(SecretId, SecretKey);SendStatusStatisticsRequest req = new SendStatusStatisticsRequest();req.setStartDateTime(2021060213L); //开始日期yyyyMMdd格式req.setEndDataTime(2021060218L); //结束日期req.setSmsSdkAppid("1400666666"); //应用idreq.setLimit(0L);           //上限和偏移量,目前为固定值0req.setOffset(0L);SendStatusStatisticsResponse resp = client.SendStatusStatistics(req);System.out.println(SendStatusStatisticsResponse.toJsonString(resp));} catch (TencentCloudSDKException e) {System.out.println(e.toString());}}/*** 回执情况短信数据统计* CallbackCount          短信回执量统计。* CallbackSuccessCount:    短信提交成功量统计。* CallbackFailCount     短信回执失败量统计。* CallbackSuccessCount        短信回执成功量统计。* InternalErrorCount      运营商内部错误统计。* InvalidNumberCount      号码无效或空号统计。* ShutdownErrorCount      停机、关机等错误统计。* BlackListCount         号码拉入黑名单统计。* 结果:* {*   "Response": {*     "CallbackStatusStatistics": {*       "CallbackCount": 0,*       "RequestSuccessCount": 0,*       "CallbackFailCount": 0,*       "CallbackSuccessCount": 0,*       "InternalErrorCount": 0,*       "InvalidNumberCount": 0,*       "ShutdownErrorCount": 0,*       "BlackListCount": 0,*       "FrequencyLimitCount": 0*     },*     "RequestId": "d4c7ba0f-e80b-45cc-8161-fe5dc590d444"*   }* }** @author zoutao* @version v2.0* @date 2021/6/2*/public static void callbackStatusStatistics(String SecretId,String SecretKey){try{SmsClient client = SampleByTxUtil.createClient(SecretId, SecretKey);CallbackStatusStatisticsRequest req = new CallbackStatusStatisticsRequest();//开始时间,yyyymmddhh 需要拉取的起始时间,精确到小时req.setStartDateTime(2021060213L);/* 结束时间,yyyymmddhh 需要拉取的截止时间,精确到小时,注:EndTime 必须大于 beginTime */req.setEndDataTime(2021060218L);req.setSmsSdkAppid("14006666666");// 设置拉取最大条数,最多100条req.setLimit(5L);/* 偏移量 注:目前固定设置为0 */req.setOffset(0L);CallbackStatusStatisticsResponse resp = client.CallbackStatusStatistics(req);System.out.println(CallbackStatusStatisticsResponse.toJsonString(resp));} catch (TencentCloudSDKException e) {System.out.println(e.toString());}}
}

service新增方法:

    /*** 发送短信验证码的接口* by 腾讯云* @param phoneNum 手机号* @param code     验证码* @param eTime    有效期*/boolean sendSmsByTx(String phoneNum, String code, Long eTime) throws Exception;

SendSmsServiceImpl新增方法:

    @Value("${tengxun.sms.secretId}")private String secretId;@Value("${tengxun.sms.secretKey}")private String secretKey;@Value("${tengxun.sms.sdkAppid}")private String sdkAppid;@Value("${tengxun.sms.sign}")private String sign;@Value("${tengxun.sms.templateId}")private String templateId;/*** 腾讯云发送短信v3.0* 注意:由于阿里云和腾讯云有些对象名称是一致的,为了防止导错包,附带了整体路径*/@Overridepublic boolean sendSmsByTx(String phoneNum, String code, Long eTime) {try {//创建客户端SmsClient client = SampleByTxUtil.createClient(secretId, secretKey);com.tencentcloudapi.sms.v20190711.models.SendSmsRequest req = new com.tencentcloudapi.sms.v20190711.models.SendSmsRequest();//下发手机号码,必须采用 E.164 标准,格式为+[国家或地区码][手机号],单次请求最多支持200个手机号。可以在前端拼接区号。//填写手机号:String[] phoneNumbers = {"+86182666666666"};String[] phoneNumbers = new String[]{"+86"+phoneNum}  ;//我们只是演示的一个手机号,若是批量,则可以采用循环放入System.out.println(Arrays.toString(phoneNumbers));//配置参数req.setPhoneNumberSet(phoneNumbers);req.setSmsSdkAppid(sdkAppid);req.setSign(sign);req.setTemplateID(templateId);/* 模板参数: 若无模板参数,则设置为空。我们的模板填了{1}{2},分别是验证码和redis有效期 */String[] templateParamSet = {code, eTime.toString()};req.setTemplateParamSet(templateParamSet);//请求发送短信com.tencentcloudapi.sms.v20190711.models.SendSmsResponse resp = client.SendSms(req);log.info("打印结果:>>"+com.tencentcloudapi.sms.v20190711.models.SendSmsResponse.toJsonString(resp));if (resp != null) {return true;}} catch (TencentCloudSDKException e) {log.error(e.toString());}return false;}

SendSmsController类新增接口:

 /*** 腾讯云发送短信接口* 请求地址:http://localhost:8080/sendSmsByTx?phoneNum=182666666** @author zoutao* @param phoneNum 手机号* @return String*/@GetMapping("/sendSmsByTx")public String sendSmsByTx(@RequestParam("phoneNum") String phoneNum) throws Exception{ValueOperations<String, String> opsForValue = redisTemplate.opsForValue();String phone = opsForValue.get(phoneNum);// 如果手机号在redis中不存在的话才进行发送验证码操作if (StringUtils.isEmpty(phone)) {// 生成4位随机数String code  = String.valueOf(Math.random()).substring(3, 7);// 设置redis有效期long eTime = 5;// 调用业务层接口 发送验证码boolean sendSmsFlag = sendSmsService.sendSmsByTx(phoneNum, code, eTime);if (sendSmsFlag) {// 发送成功之后往redis中存入该手机号以及验证码 并设置超时时间5分钟opsForValue.set(phoneNum, code, eTime, TimeUnit.MINUTES);return "发送验证码到:" + phoneNum + "成功! " + "Message:" + sendSmsFlag;}return "发送验证码失败,请检查接口配置信息。Message:" + sendSmsFlag;} else {return "该手机号:" + phoneNum + "的验证码还没过期,剩余:" + redisTemplate.getExpire(phoneNum) + "秒后可再次进行发送!";}}

启动redis服务端,启动项目,调用接口,发起get请求:
http://localhost:8080/sendSmsByTx?phoneNum=1826666666666

结果:

手机短信:


再次调用,则提示未过期:

这样,就可以完整的使用腾讯云发送短信通知了~


好了,上诉就是整个关于最新web/springboot打造通用的短信验证码微服务的过程了。

最终的效果结构图为:

三、短信常见问题(错误码一大堆)

1.阿里云的签名审核不通过?

  • 填写的内容规范一点,要求的公司资格证,百度直接白嫖啊~

2.报错:YAMLException: java.nio.charset.MalformedInputException: Input length = 1

  • 改编码:设置启动编码方式:

3.短信批量发送API(SendBatchSms)

  • 短信接收号码,JSON格式,批量上限为100个手机号码,批量调用相对于单条调用及时性稍有延迟,验证码类型的短信推荐使用单条调用的方式(即本例)。

    SendBatchSms是批量发送,传参传一个String[] phones 即可,将代码修改:

    SendSmsResponse sendResp = client.sendSms(sendSmsRequest);
    修改对应的参数和类型即可。
    SendSmsResponse sendResp = client.sendBatchSms(sendSmsRequest);
    

4.错误状态码

  • 最新的官方参考文档:https://help.aliyun.com/document_detail/102352.htm?spm=a2c4g.11186623.2.10.19e45695dzpm0g#t92654.html

  • 错误码查询:https://error-center.aliyun.com/status/product/Dysmsapi?spm=a2c4g.11186623.0.0.28a1223fJiVFcd

5.阿里云申请条件苛刻,最终还是过不了咋办?

  • 转战腾讯云,或者参考当初的第一篇文章,秒滴平台。

6.腾讯云接口报错:InvalidParameter message:The value type of parameter PhoneNumberSet.1 is not valid. requestId:797e0b53-a955-4c9c-86eb-cc7c2cd2ecc8

  • 解决:手机号码,需要加区号。如+8618899999999格式

7.其他腾讯云错误状态码:

  • https://cloud.tencent.com/document/api/382/52075

最后贴出整个项目的源码下载地址:源码

注:上述代码已经完全可以实现手机验证码服务,你可以不需要它。
放上来主要是鉴于一些小伙伴基础薄弱,提供源码,拿来即可使用,请自行下载。
没有积分的小伙伴,点个关注留下邮箱,我发给你。

最新web/springboot打造通用的短信验证码微服务(详细)相关推荐

  1. SpringBoot OAuth2.0 使用短信验证码登录授权

    SpringBoot OAuth2.0 使用短信验证码登录授权 实现步骤: 自定义授权器,继承 AbstractTokenGranter 类: 重写 getOAuth2Authentication 函 ...

  2. 手把手带你在集成SpringSecurity的SpringBoot应用中添加短信验证码登录认证功能

    本文目录 前言 1 自定义AuthenticationToken类 2 自定义AuthenticationProvider类 3 自定义MobilePhoneAuthenticationFilter ...

  3. SpringBoot 实现手机发送短信验证码

    手机发送短信 内容 一.手机发送短信 1. 前端界面代码 2. UserInfoController 控制器 3. application.properties 配置类文件 4. 具体实现 总结 内容 ...

  4. web: 手机键盘自动获取短信验证码,点击自动填充输入框

    一.展示效果: IOS手机: autocomplete="one-time-code" <van-field:class="$style.code"v-m ...

  5. Web项目中手机注册短信验证码实现的全流程及代码

    最近在做只能净化器的后台用户管理系统,需要使用手机号进行注册,找了许久才大致了解了手机验证码实现流程,今天在此和大家分享一下. 我们使用的是榛子云短信平台, 官网地址:http://smsow.zhe ...

  6. c语言短信验证码,Web项目中手机注册短信验证码实现的全流程及代码

    使用的是榛子云短信平台, 官网地址:http://smsow.zhenzikj.com 后端使用了springMvc,前端用的是jsp + jquery 下载demo: https://downloa ...

  7. springboot集成阿里云短信验证码

    1.添加pom.xml依赖 <dependency><groupId>com.aliyun</groupId><artifactId>aliyun-ja ...

  8. Java SpringBoot集成阿里云短信与邮件服务

    1.pom.xml导入jar包 <!--阿里云短信 --><dependency><groupId>com.aliyun</groupId><ar ...

  9. 对于短信验证码登录流程详细步骤

    1.构造手机验证码:使用random对象生成要求的随机数作为验证码,例如4位验证码:1000~9999之间随机数: 2.使用接口向短信平台发送手机号和验证码数据,然后短信平台再把验证码发送到制定手机号 ...

最新文章

  1. 一周AI创业:MIT博士领衔星药科技获新融资,自动驾驶再掀资本狂潮
  2. jenkins查询mysql_jenkins流水线使用mysql数据库
  3. [SpringBoot2]HelloWorld
  4. Opencv--获取Mat图像数据的方式
  5. Servlet中参数获取方法
  6. 业务编排可视化_微服务设计-服务组合和可视化编排思考
  7. linux18.04忘记账号密码,Ubuntu18.04忘记超级用户root密码,重新设置密码
  8. VMware ESXI 5.0群集+ISCSI存储
  9. 中英文对照 —— 计算机编程
  10. get与post在技术上的区别
  11. 黄国酬老师的ExtPB.Net
  12. python+django+vue酒店入住客房管理系统
  13. 亨嘉之会话数据行业未来 万字长文解码2021数据技术嘉年华
  14. Android 软件行为监控系统 的原理 主要是利用binder机制原理添加一个filter
  15. 拓嘉启远:定制类的商品如何处理退款
  16. 【用户画像和用户标签】
  17. 网易云音乐打卡2.0(一天300首)10级指日可待
  18. 安装WebSphere Application Server
  19. java常见面试题答案
  20. C语言程序设计-关系运算符和关系表达式、逻辑运算符和逻辑表达式

热门文章

  1. js数组的reduce与reduceRight方法
  2. phpcms教程:phpcms v9 筛选功能的图文教程
  3. “高调做事,高调做人”?----关于排名和排序
  4. SDI接口基于FPGA GTP实现
  5. Windows 7 重装系统后驱动不适配
  6. Semantic UI 之 表格 table
  7. uniapp蓝牙连接热敏打印机
  8. 阿里云域名申请免费DV SSL认证
  9. USB PD芯片HUSB361实现15W~65W高效低耗的快充电源设计
  10. 人人都是极客网络电子书出炉!!!