乐优商城:笔记(十):短信微服务:LySmsApplication
2019-06-04 16:09
211 查看
版权声明:转载请说明 https://blog.csdn.net/sinat_38570489/article/details/90768783
2 实现短信发送功能
文章目录
引言
注册页面上有短信发送的按钮,当用户点击发送短信,我们需要生成验证码,发送给用户。我们将使用阿里提供的阿里大于来实现短信发送。
1 创建短信微服务
因为系统中不止注册一个地方需要短信发送,因此我们将短信发送抽取为微服务:
ly-sms-service,凡是需要的地方都可以使用。
另外,因为短信发送API调用时长的不确定性,为了提高程序的响应速度,短信发送我们都将采用异步发送方式,即:
- 短信服务监听MQ消息,收到消息后发送短信。
- 其它服务要发送短信时,通过MQ通知短信微服务。
1.1 引入依赖
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>leyou</artifactId> <groupId>com.leyou.parent</groupId> <version>1.0.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <groupId>com.leyou.service</groupId> <artifactId>ly-sms</artifactId> <dependencies> <dependency> <groupId>com.aliyun</groupId> <artifactId>aliyun-java-sdk-core</artifactId> <version>4.0.6</version> </dependency> <dependency> <groupId>com.aliyun</groupId> <artifactId>aliyun-java-sdk-dysmsapi</artifactId> <version>1.1.0</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-amqp</artifactId> </dependency> <dependency> <groupId>com.leyou.common</groupId> <artifactId>ly-common</artifactId> <version>1.0.0-SNAPSHOT</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
1.2 配置文件
server: port: 8086 spring: application: name: sms-service rabbitmq: host: 192.168.124.128 username: leyou password: leyou virtual-host: /leyou redis: host: 192.168.124.128 ly: #首先把一些常量抽取到application.yml sms: accessKeyId: accessKeyId# 自己的accessKeyId accessKeySecret: AccessKeySecret# 自己的AccessKeySecret signName: 乐优商城 # 签名名称 verifyCodeTemplate: SMS_1111111111 # 模板名称,ID密码请去阿里云官方申请
1.3 启动类
@SpringBootApplication public class LySmsApplication { public static void main(String[] args) { SpringApplication.run(LySmsApplication.class); } }
1.4 属性抽取
在yml中配置好属性以后需要编写一个工具类用来读取属性信息
@ConfigurationProperties(prefix = "ly.sms") @Data public class SmsProperties { String accessKeyId; String accessKeySecret; String signName; String verifyCodeTemplate; }
@ConfigurationProperties
:
配置文件中的指定键值对映射到一个java实体类上
1.5 编写工具类
@Component @EnableConfigurationProperties(SmsProperties.class) public class SmsUtils { @Autowired private SmsProperties prop; //产品名称:云通信短信API产品,开发者无需替换 static final String product = "Dysmsapi"; //产品域名,开发者无需替换 static final String domain = "dysmsapi.aliyuncs.com"; static final Logger logger = LoggerFactory.getLogger(SmsUtils.class); public SendSmsResponse sendSms(String phone, String code, String signName, String template) throws ClientException { //可自助调整超时时间 System.setProperty("sun.net.client.defaultConnectTimeout", "10000"); System.setProperty("sun.net.client.defaultReadTimeout", "10000"); //初始化acsClient,暂不支持region化 IClientProfile profile = DefaultProfile.getProfile("cn-hangzhou", prop.getAccessKeyId(), prop.getAccessKeySecret()); DefaultProfile.addEndpoint("cn-hangzhou", "cn-hangzhou", product, domain); IAcsClient acsClient = new DefaultAcsClient(profile); //组装请求对象-具体描述见控制台-文档部分内容 SendSmsRequest request = new SendSmsRequest(); request.setMethod(MethodType.POST); //必填:待发送手机号 request.setPhoneNumbers(phone); //必填:短信签名-可在短信控制台中找到 request.setSignName(signName); //必填:短信模板-可在短信控制台中找到 request.setTemplateCode(template); //可选:模板中的变量替换JSON串,如模板内容为"亲爱的${name},您的验证码为${code}"时,此处的值为 request.setTemplateParam("{\"code\":\"" + code + "\"}"); //hint 此处可能会抛出异常,注意catch SendSmsResponse sendSmsResponse = acsClient.getAcsResponse(request); logger.info("发送短信状态:{}", sendSmsResponse.getCode()); logger.info("发送短信消息:{}", sendSmsResponse.getMessage()); return sendSmsResponse; } }
注:既然已经注入prop参数了,工具类中为什么采用参数传递的形式而不是直接prop调用get、set方法呢?
因为,如果采用prop.get()方法,短信签名、模板等参数直接被写死了,以后只能使用这一种签名和模板。
1.6 编写消息监听器
发送短信至少需要传递两个参数,一个手机号码,一个验证码,但是MQ只能接收一个参数object,那怎么办呢?我们注意到,消息体是一个Map,里面有两个属性:
- phone:电话号码
- code:短信验证码
因此我们可以把参数封装到一个Map中传递。
@Slf4j @Component @EnableConfigurationProperties(SmsProperties.class) public class SmsListener { @Autowired private SmsUtils smsUtils; @Autowired private SmsProperties prop; @RabbitListener(bindings = @QueueBinding( value = @Queue(name = "sms.verify.code.queue", durable = "true"), exchange = @Exchange(name = "ly.sms.exchange", type = ExchangeTypes.TOPIC), key = "sms.verify.code" )) public void listenInsertOrUpdate(Map<String,String> msg) { if(CollectionUtils.isEmpty(msg)){ return; } String phone = msg.remove("phone"); if(StringUtils.isBlank(phone)){ return; } smsUtils.sendSms(phone,prop.getSignName(),prop.getVerifyCodeTemplate(), JsonUtils.serialize(msg)); // 记录短信发送日志 log.info("[短信服务] 发送短信验证码,手机号:{}", phone); } }
2 实现短信发送功能
这里的业务逻辑是这样的:
- 1)我们接收页面发送来的手机号码
- 2)生成一个随机验证码
- 3)将验证码保存在服务端
- 4)发送短信,将验证码发送到用户手机
那么问题来了:验证码保存在哪里呢?
验证码有一定有效期,一般是5分钟,我们可以利用Redis的过期机制来保存。
因此修改工具类,对手机号码发送频率进行限流,以及保存验证码到Redis中:
@Slf4j @Component @EnableConfigurationProperties(SmsProperties.class) public class SmsUtils { @Autowired private SmsProperties prop; @Autowired private StringRedisTemplate redisTemplate; private final static String KEY_PREFIX = "sms:phone:"; private final static long SMS_MIN_INTERVAL_IN_MILLIS = 60000; //产品名称:云通信短信API产品,开发者无需替换 static final String product = "Dysmsapi"; //产品域名,开发者无需替换 static final String domain = "dysmsapi.aliyuncs.com"; public SendSmsResponse sendSms(String phoneNumber, String signName, String templateCode, String templateParam){ String key = KEY_PREFIX + phoneNumber; // 对手机号码发送频率进行限流 String lastTime = redisTemplate.opsForValue().get(key); if(StringUtils.isNotBlank(lastTime)){ Long last = Long.valueOf(lastTime); if(System.currentTimeMillis() - last < SMS_MIN_INTERVAL_IN_MILLIS){ log.info("[短信服务] 发送短信失败,原因:频率过高,被拦截! phoneNumber:{}", phoneNumber); return null; } } try { //可自助调整超时时间 System.setProperty("sun.net.client.defaultConnectTimeout", "10000"); System.setProperty("sun.net.client.defaultReadTimeout", "10000"); //初始化acsClient,暂不支持region化 IClientProfile profile = DefaultProfile.getProfile("cn-hangzhou", prop.getAccessKeyId(), prop.getAccessKeySecret()); DefaultProfile.addEndpoint("cn-hangzhou", "cn-hangzhou", product, domain); IAcsClient acsClient = new DefaultAcsClient(profile); //组装请求对象-具体描述见控制台-文档部分内容 SendSmsRequest request = new SendSmsRequest(); request.setMethod(MethodType.POST); //必填:待发送手机号 request.setPhoneNumbers(phoneNumber); //必填:短信签名-可在短信控制台中找到 request.setSignName(signName); //必填:短信模板-可在短信控制台中找到 request.setTemplateCode(templateCode); //可选:模板中的变量替换JSON串,如模板内容为"亲爱的${name},您的验证码为${code}"时,此处的值为 request.setTemplateParam(templateParam); //hint 此处可能会抛出异常,注意catch SendSmsResponse sendSmsResponse = acsClient.getAcsResponse(request); if (!"OK".equals(sendSmsResponse.getCode())) { log.info("[短信服务] 发送短信失败, phoneNumber:{}, 原因:{}", phoneNumber, sendSmsResponse.getMessage()); } // 发送短信成功后写入redis,并且指定生存时间为一分钟 redisTemplate.opsForValue().set(phoneNumber, String.valueOf(System.currentTimeMillis()), 1, TimeUnit.MINUTES); return sendSmsResponse; }catch (Exception e){ log.error("[短信服务] 发送短信异常, 手机号码:{}", key, e); return null; } } }
相关文章推荐
- 乐优商城:笔记(十五):订单微服务:LyOrderApplication
- 乐优商城:笔记(十三):鉴权微服务——鉴权
- 乐优商城:笔记(十一):用户微服务:LyUserApplication
- 乐优商城--服务(八) :短信微服务(LySmsApplication)
- 乐优商城:笔记(十二):鉴权微服务——授权
- 乐优商城--服务(九) :授权微服务(LyAuthApplication)--前半部分
- 【乐优商城】springcloud微服务-项目搭建
- **从零开始完成微服务项目乐优商城**
- iOS学习笔记28-系统服务(一)短信和邮件
- 扣丁学堂笔记第11天广播接收器、短信与闹钟服务
- 乐优商城--服务--优化搜索微服务和页面微服务
- iOS学习笔记28-系统服务(一)短信和邮件
- iOS学习笔记 系统服务(一)短信和邮件
- 乐优商城--服务(七) : 用户中心微服务(LyUserApplication)
- 乐优商城--关于微服务的安全问题
- 乐优商城--服务(三) : 商品微服务(LyItemApplication)--前半部分
- 乐优商城--服务(三) : 商品微服务(LyItemApplication)--后半部分
- 乐优商城--服务(四) : 上传微服务(LyUploadApplication)
- 乐优商城--服务(一): 注册微服务(LyRegister)
- 乐优商城学习笔记八-商品管理(修改商品)