2022-08-26
实战设计
0

目录

云服务
控制台配置
短信实例
短信签名
短信模板
API密钥
Java SDK 测试
Java 实现
小心有坑

此文章主要介绍 阿里云短信服务 的使用,可根据实际业务需求自行编写代码

云服务

想实现 阿里云短信服务,必须的有阿里云的账号,此处文章前的你们是不是有备而来?点击下方注册 OR 登录到阿里云

阿里云短信服务

短信服务官方帮助文档

控制台配置

登录成功后,进入到短信服务的控制台,可看到如下界面,具体菜单功能我就 不一一介绍了,相信大家都能从字面上大体了解。

image.png

下面我将开始介绍短信服务需要的主要配置 - 点击国内消息--签名管理--添加签名

短信签名:相信大家在右上角处 都能查看控制台提供的文档,我叙述的肯定不如文档写的全面,那我再此举一个通俗易懂的简单实例,帮助大家更好的去理解。

短信实例

【天猫超市】水果!1!元!包!邮!还送15元红包!仅限今天,错过后悔!椱ァ製这段描述¥rCIuYr5yL91¥后到手机淘宝 回t退订

【腾讯科技】王者荣耀四周岁庆典明日开启,参与活动得婉儿周年庆限定,26日皮肤返场,登录抽百万内测!url.cn/5vKYnEb  回T退订

此处是我收到的2条短信,其中【天猫超市】与【腾讯科技】即为签名,大家可以 查看一下自己手机里面的短信,是不是发现每条短信开头部分都是【XXXX】,是不是发现自己以前从来没 注意过观察过,现在看来是不是特别神奇?

短信签名

image.png

image.png

上面的申请签名的过程我就不过多叙述了,大家应该都能轻易获取到,下面我将介绍 短信模板

短信模板

短信模版,即具体发送的短信内容。

image.png

此处我也不再举例,相信大家根据上面实例都能理解的很透彻。

API密钥

签名与模板配置完后,在实际开发中还需要添加访问阿里API的密钥

首先点击右上角用户,添加一个处理短信的用户

image.png

添加完成后,自动跳转到用户信息页面,自行保存 AccessKeySecret ,关闭页面后,你将再也看不到 AccessKeySecret,这个很重要

注意

此处的账户信息AccessKey ID 与 AccessKeySecret 在项目中进行使用,请谨慎保存

给账户授权, 使账户可以管理短信服务

image.png

Java SDK 测试

当 签名与 模板 申请下来以后,可以在线进行测试,具体参数介绍请查看API文档

image.png

此时收到短信了没?写到此处,控制台 配置 与 测试 便结束了,相信大家的签名与模板都能申请下来,接下来我们将进入代码的世界,准备好了吗?

Java 实现

上面 SDK Demo中已经给出实例,我将结合实际需求写一个小Demo;

需求:发送短信验证码进行用户注册,且60秒内不能重复发送短信,验证码有效期为5分钟

环境准备:装有 RabbitMQ,Redis 环境的虚拟机

1.pom文件

xml
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <!--lombok--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <!--阿里云短信服务--> <dependency> <groupId>com.aliyun</groupId> <artifactId>aliyun-java-sdk-core</artifactId> <version>4.1.0</version> </dependency> <!--阿里云json转换包--> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.43</version> </dependency> <!--redis--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <!--rabbitMq服务--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-amqp</artifactId> </dependency>

2.application.yml配置(IP、端口 根据实际环境自行配置)

yaml
spring: #redis地址 端口号默认6379 redis: host: 192.168.80.129 #rabbitmq配置 rabbitmq: host: 192.168.80.131 port: 5672 username: guest password: guest publisher-confirms: true # 消息发送到交换机确认机制,是否确认回调 #自定义配置项 sms: accessKeyId: 填写控制台配置用户时,记录的用户accessKeyId accessSecret: 填写控制台配置用户时,记录的用户accessSecret signName: 申请的签名 #签名 templateCode: 模板的CODE #模板CODE domain: dysmsapi.aliyuncs.com action: SendSms #API 的名称

3.常量定义

java
/** * 常量定义 * @author: diego * @create: 2019/10/9 */ public class DataDict { //redis-key 手机号限流前缀 public final static String SMS_PHONE_NUMBER = "sms:phone:"; //redis-key 验证码前缀 public final static String SMS_PHONE_VERIFICATION = "sms:phone:verification:"; }

4.属性定义

java
import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; /** * 配置项属性 * @author: diego * @create: 2019/10/9 */ @Data @Component @ConfigurationProperties(prefix = "sms") public class SmsProperties { private String accessKeyId; private String accessSecret; private String SignName; private String templateCode; private String domain; private String action; @Override public String toString() { return "SmsProperties{" + "accessKeyId='" + accessKeyId + '\'' + ", accessSecret='" + accessSecret + '\'' + ", SignName='" + SignName + '\'' + ", templateCode='" + templateCode + '\'' + ", domain='" + domain + '\'' + ", action='" + action + '\'' + '}'; } }

5.短信工具类

java
import com.alibaba.fastjson.JSON; import com.aliyuncs.CommonRequest; import com.aliyuncs.CommonResponse; import com.aliyuncs.DefaultAcsClient; import com.aliyuncs.IAcsClient; import com.aliyuncs.exceptions.ClientException; import com.aliyuncs.exceptions.ServerException; import com.aliyuncs.http.MethodType; import com.aliyuncs.profile.DefaultProfile; import com.aliyuncs.utils.StringUtils; import com.example.demo.dict.DataDict; import lombok.extern.slf4j.Slf4j; import lombok.val; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.stereotype.Component; import java.util.Map; import java.util.concurrent.TimeUnit; /** * 短信发送工具类 * @author: diego * @create: 2019/10/9 */ @Slf4j @Component @EnableConfigurationProperties(SmsProperties.class) public class SmsUtils { @Autowired SmsProperties smsProperties; @Autowired StringRedisTemplate redisTemplate; /** * 短信发送方法 * demo:https://api.aliyun.com/?spm=a2c4g.11186623.2.13.63ee50a4NKRQsm#/?product=Dysmsapi&lang=JAVA * @param phoneNumbers * @param signName * @param templateCode * @return */ public CommonResponse SendSms(String phoneNumbers,String signName,String templateCode,String templateParam){ try { //限流校验 val s = redisTemplate.boundValueOps(DataDict.SMS_PHONE_NUMBER + phoneNumbers).get(); if(StringUtils.isNotEmpty(s)){ System.err.println(s); if (System.currentTimeMillis() - Long.valueOf(s) < 60000){ log.info("手机号码发送频繁,稍后再试!"); return null; } } DefaultProfile profile = DefaultProfile.getProfile("cn-hangzhou", smsProperties.getAccessKeyId(), smsProperties.getAccessSecret()); IAcsClient client = new DefaultAcsClient(profile); CommonRequest request = new CommonRequest(); request.setMethod(MethodType.POST); request.setDomain(smsProperties.getDomain()); request.setVersion("2017-05-25"); //当前日期 格式:yyyy-MM-dd request.setAction(smsProperties.getAction()); request.putQueryParameter("PhoneNumbers", phoneNumbers); request.putQueryParameter("SignName", signName); request.putQueryParameter("TemplateCode", templateCode); if(StringUtils.isNotEmpty(templateParam)){ request.putQueryParameter("TemplateParam", templateParam); } CommonResponse response = client.getCommonResponse(request); Map<String,String> map = JSON.parseObject(response.getData(),Map.class); if("OK".equals(map.get("Code"))){ log.info("短信发送成功!返回消息:"+response.getData()); //短信发送成功,设置计时器 开始限流 redisTemplate.boundValueOps(DataDict.SMS_PHONE_NUMBER + phoneNumbers).set( String.valueOf(System.currentTimeMillis()),1, TimeUnit.MINUTES); }else{ log.info("短信发送失败!状态码为:"+map.get("Code")+" 错误信息为:"+map.get("Message")); } return response; } catch (ServerException e) { e.printStackTrace(); log.info("短信发送失败!服务异常信息为:"+ e); } catch (ClientException e) { e.printStackTrace(); log.info("短信发送失败!客户端异常信息为:"+ e); } catch (Exception e){ e.printStackTrace(); log.info("短信发送失败!异常信息为:"+ e); } return null; } }

6.短信监听

java
import com.alibaba.fastjson.JSON; import com.example.demo.dict.DataDict; import com.example.demo.sms.SmsProperties; import com.example.demo.sms.SmsUtils; import org.springframework.amqp.core.ExchangeTypes; import org.springframework.amqp.rabbit.annotation.Exchange; import org.springframework.amqp.rabbit.annotation.Queue; import org.springframework.amqp.rabbit.annotation.QueueBinding; import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; import java.util.Map; /** * 短信监听 * @author: diego * @create: 2019/10/9 */ @Component public class SmsListener { @Autowired SmsProperties smsProperties; @Autowired SmsUtils smsUtils; @RabbitListener( bindings = @QueueBinding( value = @Queue( value = "sms_amqp_queue",durable = "true"), exchange = @Exchange( value = "sms_amqp_exchange", type = ExchangeTypes.TOPIC), key = {"#.#"} ) ) public void listenSmsSend(Map<String,String> map){ //校验map if(CollectionUtils.isEmpty(map)){ return; } //从map中删除key为phone 返回key的值 String phone = map.remove("phone"); //校验返回的key为phone的返回值 if (StringUtils.isEmpty(phone)){ return; } System.out.println(JSON.toJSONString(map)); smsUtils.SendSms(phone,smsProperties.getSignName(), smsProperties.getTemplateCode(), JSON.toJSONString(map)); } }

7.测试

java
import com.example.demo.dict.DataDict; import com.example.demo.sms.SmsProperties; import com.example.demo.sms.SmsUtils; import lombok.val; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.amqp.rabbit.support.CorrelationData; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.test.context.junit4.SpringRunner; import java.text.SimpleDateFormat; import java.util.Date; import java.util.HashMap; import java.util.Map; import java.util.concurrent.TimeUnit; @RunWith(SpringRunner.class) @SpringBootTest public class DemoApplicationTests { @Autowired SmsProperties smsProperties; @Autowired StringRedisTemplate redisTemplate; @Autowired SmsUtils smsUtils; @Autowired RabbitTemplate rabbitTemplate; /** * 获取Application.yml中短信参数配置测试 */ @Test public void propertiesTest() { System.err.println(smsProperties.toString());; } /** * redis连通性测试 */ @Test public void redisTest() { redisTemplate.boundValueOps("test").set("测试redis是否可用!"); val test = redisTemplate.boundValueOps("test").get(); System.out.println(test); } /** * 短信发送服务测试 */ @Test public void smsSend() { smsUtils.SendSms("1510110XXXX",smsProperties.getSignName(), smsProperties.getTemplateCode(),"{\"code\":\"888888\"}"); } /** * 消息队列 - 短信发送服务测试 */ @Test public void smsSendToRabbitMQ() { Map<String,String> map = new HashMap<>(); map.put("phone","1510110XXXX"); map.put("code","888888"); rabbitTemplate.convertAndSend("sms_amqp_exchange", "a.b", map); //短信验证码有效期 redisTemplate.boundValueOps(DataDict.SMS_PHONE_VERIFICATION+"1510110XXXX").set("888888",5, TimeUnit.MINUTES); } }

此处的代码可以根据阿里提供的官方Demo,按照实际业务需求自行编写;

小心有坑

阅读到这里,你是否有一个疑问,我并没有在控制台中购买短信服务套餐,为什么短信发送成功了呢?

哈哈,这是一个比较深的坑,大家此时查看一下控制台中 费用统计,短信服务是预扣,已经开始欠费了

image.png

点击上方费用按钮,可查看账户信息,进入账户充值即可

image.png

短信服务的账单是 每月一结算,记得保证账户余额充足^_^。

本文作者:柳始恭

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!