主要思路:
将自己生成的验证码放进activemq中的一个消息队列中(点对点),于此同时,将手机号,验证码以<手机号,验证码>存入redis中,springboot监听该队列,在springboot中接入短信服务的api ,实现发送短信,最后验证时只需判断页面的验证码是否和Redis中键值相匹配。成功将该用户的信息写入mysql数据库。
代码实现及步骤解析
一,创建springBoot服务 (打包方式为jar)
1,添加web,activemq,热部署,腾讯云短信服务的依赖
腾讯云短信服务依赖
com.github.qcloudsms
qcloudsms
1.0.5
注:只有依赖是没用的,还要去申请使用所需的一些材料:短信签名,短信模板,(如果自己玩的话,建议使用腾讯云,比较好申请,验证申请要一个微信公共号的管理界面截图,具备以下图例中的东西才能发短信)
只有上述的东西准备好才能使用人家的服务。
2,下载对应的sdk解压,在esclipse测试下就行了,如果不成功大多数是配置参数有问题,腾讯短信对应的java sdk说的很明白。
3,创建springBoot的引导类Application.java
springboot内置了tomcat ,所以不需要在tomcat等容器内启动,添加一些依赖就可以
package itcast_sms_service;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
4,创建短信工具类SmsUtil.java
@Component
public class SmsUtil {
@Autowired
private Environment env;
//发送短信
public void sendSms(String phoneNumbers,String templateId,ArrayList params,String smsSign) {
try {
SmsSingleSender ssender = new SmsSingleSender(Integer.parseInt(env.getProperty("appid")), env.getProperty("appkey"));
SmsSingleSenderResult result = ssender.sendWithParam("86",
phoneNumbers,
Integer.parseInt(templateId),
params,
smsSign, "", "");
System.out.print(result);
} catch (HTTPException e) {
// HTTP响应码错误
e.printStackTrace();
} catch (JSONException e) {
// json解析错误
e.printStackTrace();
} catch (IOException e) {
// 网络IO错误
e.printStackTrace();
}
}
}
这里主要是一个类 的和一个方法的参数,根据需要的类型填入就行了
sendWithParam(String nationCode, String phoneNumber, int templateId, ArrayList params, String sign, String extend, String ext)
Parameters:nationCode 国家码,如 86 为中国phoneNumber 不带国家码的手机号templateId 信息内容
params 模板参数列表,如模板 {1}…{2}…{3},那么需要带三个参数
sign 签名,如果填空,系统会使用默认签名
extend 扩展码,可填空
ext 服务端原样返回的参数,可填空
这些参数例如appid 和appkey在application.properties 中配置,用自动注入的Environment 进行取值。
5,创建消息监听类SmsListener.Java
监听activeMq对应队列的消息,并调用SmsUtil的sendSms(****);
这里用map来接受消息,那在生产者也得用map封装数据
@Component
public class SmsListener {
//注入工具类
@Autowired
private SmsUtil smsUtil;
//配置监听的消息队列
@JmsListener(destination="sms")
public void onMessage(Map map) {
String phoneNumbers = map.get("phoneNumbers").toString();
String templateId = map.get("templateId").toString();
ArrayList params = new ArrayList<>();
String[] string = ((String) map.get("params")).split(" ");
params.add(string[0]);
params.add(string[1]);
String smsSign = (String) map.get("smsSign");
smsUtil.sendSms(phoneNumbers,templateId,params,smsSign);
smsUtil.receiveSms();
System.out.println(map);
}
}
由于springboot有内置的activemq,但是要使用自己的就必须在配置文件中添加
server.port=9003//端口
spring.activemq.broker-url=tcp://192.168.72.129:61616
appid=****
appkey=****
templateId=****
smsSign=****
至此,短信微服务ok ,这是一个独立的服务。
二,根据业务需求,创建生产者,也就是项目中需要短信服务的模块,一般都在注册页面中使用,新用户注册实际就是在数据库中添加一条信息。我的项目用的dubbox架构(面向服务),在注册这个模块中向dubbox注册userService服务,在dubbox中userService则为服务的提供者,模块的web层是服务的消费者,也就是userController,userController在页面接受到页面的请求,如:发送验证码(电话号等信息将返回),验证码校验(电话号,用户输入的验证码等信息将返回), 完成注册 等。通过dubbox向userService服务请求具体的方法,如:发送验证码,验证码校验。而userService服务则向activemq发送消息:我要发送一条消息,消息的内容是什么,等信息。然后,springboot短信服务会监听到该消息 ,根据消息的具体内容,发送短信到用户。在验证码校验阶段,由于在请求发送验证码时,userService就把产生的验证码或电话号 存入Redis数据库中,在去向activemq发消息,所以在验证时只需要校验用户输入的验证码是否Redis数据库的电话号是否一致。
以上是注册模块的大致流程图。主要代码如下
1,前端采用angularjs框架,用户点击发送验证码,
页面
获取短信验证码
前端service层
//发送验证码
this.sendCode=function(phone){
return $http.get("../user/sendCode.do?phone="+phone);
}
前端控制层,调用前端service层
//发送验证码
$scope.sendCode=function(){
if($scope.entity.phone==null){
alert("请输入手机号");
return;
}
userService.sendCode($scope.entity.phone).success(
function(response){
alert(response.message);
}
);
}
2,后端采用springmvc
web层,注入服务层UserService
//发送验证码
@RequestMapping("/sendCode")
public Result sendCode(String phone) {
if(!PhoneFormatCheckUtils.isChinaPhoneLegal(phone)) {
return new Result(false, "手机格式不正确");
}
try {
userService.createCode(phone);//创建验证码
return new Result(true, "短信验证吗发送成功");
} catch (Exception e) {
e.printStackTrace();
return new Result(true, "短信验证码发送失败");
}
}
service层 发送消息并保存数据到Redis
需要注入Redis和 jmsTemplate消息服务,目的队列名smsDestination(这是在配置文件中配置的,点对点)
注意的是,这里验证码的生成方式是用随机函数,也可以用别的策略,这种不是最好的,有时间试一试别的
//生成验证码并发送到消息队列
@Override
public void createCode(final String phone) {
//生成6位随机数
final String code = (long) (Math.random()*1000000)+"";
System.out.println("验证码====="+code);
redisTemplate.boundHashOps("smsCode").put(phone, code);
System.out.println(phone);//便于后台验证
System.out.println(templateId+smsSign);
//短信模板参数
//发送activemq
jmsTemplate.send(smsDestination, new MessageCreator() {
@Override
public Message createMessage(Session session) throws JMSException {
MapMessage mapMessage = session.createMapMessage();
mapMessage.setString("phoneNumbers", phone);//电话号
mapMessage.setString("templateId", templateId);//模板id
mapMessage.setString("smsSign", smsSign);//签名id
mapMessage.setString("params", code+" "+"1");
System.out.println("参数"+code);
return mapMessage;
}
});
}
至此,发送验证码的功能就ok了,可以看到,各个功能,服务的耦合度很低 ,各个模块之间的联系都是通过网络进行的,远程调用(rpc),例如在发送验证码的过程中,userService 服务通过activemq进行发送短信验证码,这样做就可以减少服务器的压力,提高该服务的效率,他不必关心怎样去发送短信,也不需要等待响应,提高了用户体验。这里用Redis数据库作验证数据的存储,读取时间短就不用说了,可以为其设置过时间,如果使用mysql的话,会增加主要业务数据访问的压力,过期时间没法解决,除非删除数据,这又带来了数据库的访问压力。Redis存储的数据结构多样,用mysql没那么方便。
关于验证码 的校验就是配置SpringDateRedis调用 redisTemplate的方法,对系统的验证码和用户输入的验证码进行比较,一致则成功。整个功能的思路很重,
附上成功的图片:
如果有不大对的地方,望批证。