实现前提
服务器搭建Redis以及RabbitMQ环境(尽量使用management版本)
在浏览器输入http://(你的服务器地址):15672/#/
默认账号密码为:guest
登陆之后创建队列,我们发送短信,一般都是一对一,所以选择直接模式
使用RabbitMQ给用户发一份验证码,在Redis中存一份,当用户提交注册请求时,两者去匹配
服务器搭建Redis环境(本文省略)
技术栈:SpringBoot2.0、redis、rabbitmq、
在阿里云网站申请短信服务,添加签名,添加模板,创建AccessKey,下载或复制短信发送的JAVA版本的SDK代码
具体代码实现
User层pom文件
<dependencies>
<dependency>
<groupId>com.pure</groupId>
<artifactId>pure_common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<version>2.2.5.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-amqp -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
<version>2.2.1.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.11</version>
</dependency>
</dependencies>
本系统为前后端分离项目
Controller层代码实现:
/**
* /sendsms/{mobile}
* 发送手机验证码
*/
@RequestMapping ( value = "/sendsms/{mobile}", method = RequestMethod.POST )
public Result sendSms ( @PathVariable String mobile ) {
userService.sendSms ( mobile );
return new Result ( true,StatusCode.SMS_SUCCESS );
}
/**
* /register/{code}
* 注册用户
*/
@RequestMapping ( value = "/register/{code}", method = RequestMethod.POST )
public Result register ( @RequestBody User user,@PathVariable String code ) {
//手机号怎么拿,在填写注册信息的时候,得封装道一个对象中
//在注册过程中的要去验证手机验证码是否正确,如果缓存中没有,证明是瞎写的
String checkCodeRedis = (String) redisTemplate.opsForValue ( ).get ( "checkCode_" + user.getMobile ( ) );
if (checkCodeRedis.isEmpty ( )) {
return new Result ( false,StatusCode.ERROR );
}
if (!checkCodeRedis.equals ( code )){
return new Result ( false ,StatusCode.CODE_ERROR );
}
userService.add ( user );
return new Result ( true,StatusCode.ADD_USER_SUCCESS );
}
两个接口,一个为短信发送,一个为用户注册【注:其中判断验证码的我是写在控制层,这样也可以,也可以写在业务层】
Service层业务实现
/**
* 增加,注册用户
*
* @param user
*/
public void add ( User user ) {
user.setId ( idWorker.nextId ( ) + "" );
//把判断放在controller层,不管是在业务层还是在控制层,都可以
user . setFollowcount(0);//关注数
user . setFanscount(0);//粉丝数
user.setOnline ( 0L );
user . setRegdate(new Date());//注册 日期
user . setUpdatedate(new Date());//更新日期
user . setLastdate(new Date());//最后 登陆日期
userDao.save ( user );
}
//发送手机验证码
//生成六位随机数
//这里可能会出现一个异常,已解决
String checkCode = RandomStringUtils.randomNumeric ( 6 );
//这里生成随机六位数,直接使用阿帕奇的common-lang3,代码更简洁
//给缓存发一份
redisTemplate.opsForValue ( ).set ( "checkCode_" + mobile,checkCode,5,TimeUnit.MINUTES );
//用户发一份
Map < String, String > SmsMap = new HashMap <> ( );
SmsMap.put ( "mobile",mobile );
SmsMap.put ( "checkCode",checkCode );
rabbitTemplate.convertAndSend ( "pure_sms",SmsMap );
//在控制器输出一份【方便测试】
System.out.println ( "验证码为:" + checkCode );
//这个输出是为了更方便测试,具体实施的时候,要删掉,不然不你想干嘛,用户发一份,我这里收到一份,赶紧注册把钱全部给转走??哈哈哈哈这样不行的喔
}
```java
server:
port: 1008
spring:
application:
name: pure-user #指定服务名
datasource:
driverClassName: com.mysql.jdbc.Driver
url:
username:
password:
jpa:
database: MySQL
show-sql: true
redis:
host: 192.168.232.134
rabbitmq:
host: 192.168.232.134
我的这是微服务项目
前面的注册,短信发送的控制业务写完了
现在我们需要写短信微服务
所需pom文件
<dependencies>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-amqp -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
<version>2.2.1.RELEASE</version>
</dependency>
<!-- <dependency>-->
<!-- <groupId>com.aliyun</groupId>-->
<!-- <artifactId>dysmsapi20170525</artifactId>-->
<!-- <version>2.0.1</version>-->
<!-- </dependency>-->
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>tea-openapi</artifactId>
<version>0.0.11</version>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>tea-console</artifactId>
<version>0.0.1</version>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>tea-util</artifactId>
<version>0.2.10</version>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>tea</artifactId>
<version>[1.0.3, 2.0.0)</version>
</dependency>
<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>
</dependencies>
前面我们在阿里云下载了一个SDK工具类,我的和那个有点区别
我两个都拿出来,参考者用哪个都行
package com.pure.sms.utils;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.dysmsapi.model.v20170525.QuerySendDetailsRequest;
import com.aliyuncs.dysmsapi.model.v20170525.QuerySendDetailsResponse;
import com.aliyuncs.dysmsapi.model.v20170525.SendSmsRequest;
import com.aliyuncs.dysmsapi.model.v20170525.SendSmsResponse;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.profile.DefaultProfile;
import com.aliyuncs.profile.IClientProfile;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* 短信工具类
* @author pure
*
*/
@Component
public class SmsUtil {
//产品名称:云通信短信API产品,开发者无需替换
static final String product = "Dysmsapi";
//产品域名,开发者无需替换
static final String domain = "dysmsapi.aliyuncs.com";
@Autowired
private Environment env;
// TODO 此处需要替换成开发者自己的AK(在阿里云访问控制台寻找)
/**
* 发送短信
* @param mobile 手机号
* @param template_code 模板号
* @param sign_name 签名
* @param param 参数
* @return
* @throws ClientException
*/
public SendSmsResponse sendSms( String mobile,String template_code,String sign_name,String param) throws ClientException {
String accessKeyId =env.getProperty("aliyun.sms.accessKeyId");
String accessKeySecret = env.getProperty("aliyun.sms.accessKeySecret");
//可自助调整超时时间
System.setProperty("sun.net.client.defaultConnectTimeout", "10000");
System.setProperty("sun.net.client.defaultReadTimeout", "10000");
//初始化acsClient,暂不支持region化
IClientProfile profile = DefaultProfile.getProfile("cn-hangzhou", accessKeyId, accessKeySecret);
DefaultProfile.addEndpoint("cn-hangzhou", "cn-hangzhou", product, domain);
IAcsClient acsClient = new DefaultAcsClient(profile);
//组装请求对象-具体描述见控制台-文档部分内容
SendSmsRequest request = new SendSmsRequest();
//必填:待发送手机号
request.setPhoneNumbers(mobile);
//必填:短信签名-可在短信控制台中找到
request.setSignName(sign_name);
//必填:短信模板-可在短信控制台中找到
request.setTemplateCode(template_code);
//可选:模板中的变量替换JSON串,如模板内容为"亲爱的${name},您的验证码为${code}"时,此处的值为
request.setTemplateParam(param);
//选填-上行短信扩展码(无特殊需求用户请忽略此字段)
//request.setSmsUpExtendCode("90997");
//可选:outId为提供给业务方扩展字段,最终在短信回执消息中将此值带回给调用者
request.setOutId("yourOutId");
//hint 此处可能会抛出异常,注意catch
SendSmsResponse sendSmsResponse = acsClient.getAcsResponse(request);
return sendSmsResponse;
}
public QuerySendDetailsResponse querySendDetails( String mobile,String bizId) throws ClientException {
String accessKeyId =env.getProperty("accessKeyId");
String accessKeySecret = env.getProperty("accessKeySecret");
//可自助调整超时时间
System.setProperty("sun.net.client.defaultConnectTimeout", "10000");
System.setProperty("sun.net.client.defaultReadTimeout", "10000");
//初始化acsClient,暂不支持region化
IClientProfile profile = DefaultProfile.getProfile("cn-hangzhou", accessKeyId, accessKeySecret);
DefaultProfile.addEndpoint("cn-hangzhou", "cn-hangzhou", product, domain);
IAcsClient acsClient = new DefaultAcsClient(profile);
//组装请求对象
QuerySendDetailsRequest request = new QuerySendDetailsRequest();
//必填-号码
request.setPhoneNumber(mobile);
//可选-流水号
request.setBizId(bizId);
//必填-发送日期 支持30天内记录查询,格式yyyyMMdd
SimpleDateFormat ft = new SimpleDateFormat("yyyyMMdd");
request.setSendDate(ft.format(new Date()));
//必填-页大小
request.setPageSize(10L);
//必填-当前页码从1开始计数
request.setCurrentPage(1L);
//hint 此处可能会抛出异常,注意catch
QuerySendDetailsResponse querySendDetailsResponse = acsClient.getAcsResponse(request);
return querySendDetailsResponse;
}
}
上面的是我的工具类,下面的是阿里云下载的SDK
// This file is auto-generated, don't edit it. Thanks.
package com.aliyun.sample;
import com.aliyun.tea.*;
import com.aliyun.dysmsapi20170525.*;
import com.aliyun.dysmsapi20170525.models.*;
import com.aliyun.teaopenapi.*;
import com.aliyun.teaopenapi.models.*;
import com.aliyun.teaconsole.*;
import com.aliyun.teautil.*;
public class Sample {
/**
* 使用AK&SK初始化账号Client
* @param accessKeyId
* @param accessKeySecret
* @return Client
* @throws Exception
*/
public static com.aliyun.dysmsapi20170525.Client createClient(String accessKeyId, String accessKeySecret) throws Exception {
Config config = new Config()
// 您的AccessKey ID
.setAccessKeyId(accessKeyId)
// 您的AccessKey Secret
.setAccessKeySecret(accessKeySecret);
// 访问的域名
config.endpoint = "dysmsapi.aliyuncs.com";
return new com.aliyun.dysmsapi20170525.Client(config);
}
public static void main(String[] args_) throws Exception {
java.util.List<String> args = java.util.Arrays.asList(args_);
com.aliyun.dysmsapi20170525.Client client = Sample.createClient("ACCESS_KEY_ID", "ACCESS_KEY_SECRET");
SendSmsRequest sendSmsRequest = new SendSmsRequest();
SendSmsResponse resp = client.sendSms(sendSmsRequest);
com.aliyun.teaconsole.Client.log(com.aliyun.teautil.Common.toJSONString(TeaModel.buildMap(resp)));
}
}
还有相对应的pom
<dependencies>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>dysmsapi20170525</artifactId>
<version>2.0.1</version>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>tea-openapi</artifactId>
<version>0.0.11</version>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>tea-console</artifactId>
<version>0.0.1</version>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>tea-util</artifactId>
<version>0.2.10</version>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>tea</artifactId>
<version>[1.0.3, 2.0.0)</version>
</dependency>
</dependencies>
SMS服务结构如上
别忘了配置文件
server:
port: 1009
spring:
application:
name: pure-sms #指定服务名
rabbitmq:
host: 192.168.232.134
aliyun:
sms:
accessKeyId:这里是你阿里云创建的accessKeyId
accessKeySecret: 还有这个
template_code:这是是你的阿里云短信模板
sign_name:这是你的阿里云短信签名
这些弄好之后,就可以写MQ的队列监听器了,直接上代码
package com.pure.sms.listener;
import com.aliyuncs.exceptions.ClientException;
import com.pure.sms.utils.SmsUtil;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.util.Map;
/**
* @author 刘乐锋
* @create 2021/3/7 21:37
*/
//消息队列监听器
@Component
@RabbitListener ( queues = "pure_sms" ) //让它是消费者
public class SmsListener {
@Autowired
private SmsUtil smsUtil;
@Value ( "${aliyun.sms.template_code}" )
private String template_code;
@Value ( "${aliyun.sms.sign_name}" )
private String sign_name;
@RabbitHandler
public void executeSms( Map<String,String> map ){
String mobile = map.get ( "mobile" );
String checkCode= map.get ( "checkCode" );
System.out.println ("手机号:"+map.get ( "mobile" ) );
System.out.println ("验证码:"+map.get ("checkCode" ) );
try {
smsUtil.sendSms ( mobile, template_code,sign_name,"{\"checkcode\": \""+checkCode+"\"}");
} catch (ClientException e) {
e.printStackTrace ( );
}
}
}
两个输出可以删除,这里方便测试才写
之后就可以运行实现了,记得保证阿里云账户有钱,10快就可以玩好久,土豪随意