脚本之家

电脑版
提示:原网页已由神马搜索转码, 内容由www.jb51.net提供.
您的位置:首页软件编程java→ Springboot整合RabbitMQ发送验证码

Springboot整合RabbitMQ实现发送验证码的示例代码

  更新时间:2022年02月10日 11:31:51  作者:weixin_43873712 
这篇文章主要介绍了Springboot整合RabbitMQ实现发送验证码的功能,基于AMQP协议实现的消息队列,它是一种应用程序之间的通信方法,消息队列在分布式系统开 发中应用非常广泛,需要的朋友可以参考下

1. RabbitMQ的介绍

  • MQ全称为Message Queue,即消息队列, RabbitMQ是由erlang语言开发,基于AMQP(Advanced Message Queue 高级消息队列协议)协议实现的消息队列,它是一种应用程序之间的通信方法,消息队列在分布式系统开 发中应用非常广泛。RabbitMQ官方地址:http://www.rabbitmq.com/
  • 开发中消息队列通常有如下应用场景:

1、任务异步处理。 将不需要同步处理的并且耗时长的操作由消息队列通知消息接收方进行异步处理。提高了应用程序的响应时间。

2、应用程序解耦合 MQ相当于一个中介,生产方通过MQ与消费方交互,它将应用程序进行解耦合。并且有如下优点。

1.使得简单,功能强大。

2.基于AMQP协议。

3.社区活跃,文档完善。

4.高并发性能好,这主要得益于Erlang语言。

5.Spring Boot默认已集成RabbitMQ

  • 组成部分说明如下:
  • Broker:消息队列服务进程,此进程包括两个部分:
  • Exchange和Queue。
  • Exchange:消息队列交换机,按一定的规则将消息路由转发到某个队列,对消息进行过虑。

Queue:消息队列,存储消息的队列,消息到达队列并转发给指定的消费方。

Producer:消息生产者,即生产方客户端,生产方客户端将消息发送到MQ。

Consumer:消息消费者,即消费方客户端,接收MQ转发的消息。

消息发布接收流程:

-----发送消息-----

1、生产者和Broker建立TCP连接。

2、生产者和Broker建立通道。

3、生产者通过通道消息发送给Broker,由Exchange将消息进行转发。

4、Exchange将消息转发到指定的Queue(队列)

----接收消息-----

1、消费者和Broker建立TCP连接

2、消费者和Broker建立通道

3、消费者监听指定的Queue(队列)

4、当有消息到达Queue时Broker默认将消息推送给消费者。

5、消费者接收到消息

2. 搭建环境

  • 实现原理:
  • 在绑定(Binding)Exchange与Queue的同时,一般会指定一个binding key;消费者将消息发送给Exchange时,一般会指定一个routing key;当binding key与routing key相匹配时,消息将会被路由到对应的Queue中。
  • 多个消费者可以订阅同一个Queue,这时Queue中的消息会被平均分摊给多个消费者进行处理,而不是每个消费者都收到所有的消息并处理。

2.1引入jar包

 <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<!--redis-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

2.2生产者配置

2.2.1Rabbit配置类

package com.cui.user.config;
import org.springframework.amqp.core.*;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/** rabbitmq配置类 配置交换机,消息队列,并且绑定交换机和queue
* @Author Cui
* @Date 2020-4-9 14:55
*/
@Configuration
public class RabbitmqConfig {
//队列bean的名称 cms 用来发送短信验证码
public static final String QUEUE_INFORM_CMS= "queue_inform_cms";
//队列bean的名称 email 用来发送邮件
//public static final String QUEUE_INFORM_EMAIL= "queue_inform_email";
//交换机的名称
public static final String EXCHANGE_TOPIC_INFORM_="exchange_topic_inform";
//队列的名称
@Value("${cxp.mq.queue}")
public String queue_cms_postpage_name;
//routingKey
@Value("${cxp.mq.routingKey}")
public String routingKey;
/**
* 交换机配置使用direct类型
* @return the exchange
*/
@Bean(EXCHANGE_TOPIC_INFORM_)
public Exchange EXCHANGE_TOPICS_INFORM() {
//durable(true) 持久化,mq重启之后交换机还在
return ExchangeBuilder.directExchange(EXCHANGE_TOPIC_INFORM_).durable(true).build();
}
//声明队列
@Bean(QUEUE_INFORM_CMS)
public Queue QUEUE_CMS_POSTPAGE() {
Queue queue = new Queue(QUEUE_INFORM_CMS);
return queue;
* 绑定队列到交换机
*
* @param queue the queue
* @param exchange the exchange
* @return the binding
@Bean
public Binding BINDING_QUEUE_INFORM_SMS(@Qualifier(QUEUE_INFORM_CMS) Queue queue, @Qualifier(EXCHANGE_TOPIC_INFORM_) Exchange exchange) {
return BindingBuilder.bind(queue).to(exchange).with(routingKey).noargs();
}

2.2.2 application.yml文件配置

server:
port: ${PORT:8002}
spring:
application:
name: cxp-service-manage-user
#Redis配置
redis:
host: 127.0.0.1
port: 6379
jedis:
pool:
max-active: 8
max-wait: -1
max-idle: 500
min-idle: 0
lettuce:
shutdown-timeout: 0
datasource:
url: jdbc:mysql://localhost:3306/system_user?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver
rabbitmq:
port: 5672
username: guest
password: guest
virtualHost: /
cxp:
mq:
#cms客户端监控的队列名称(不同的客户端监控的队列不能重复)
queue: queue_inform_cms
routingKey: inform.#.sms.# #此routingKey邮件消费者和信息消费者通用
mybatis:
mapper-locations: classpath:mapper/*Mapper.xml
type-aliases-package: com.cui.model.entity.user
mapper:
mappers: com.cui.model.BaseMapper #通用基类配置
identity: mysql
pagehelper:
helperDialect: mysql
reasonable: true
supportMethodsArguments: true
params: count=countSql
eureka:
client:
registerWithEureka: true #服务注册开关
fetchRegistry: true #服务发现开关
serviceUrl: #Eureka客户端与Eureka服务端进行交互的地址,多个中间用逗号分隔
defaultZone: ${EUREKA_SERVER:http://localhost:50101/eureka/,http://localhost:50102/eureka/}
instance:
prefer-ip-address: true #将自己的ip地址注册到Eureka服务中
ip-address: ${IP_ADDRESS:127.0.0.1}
instance-id: ${spring.application.name}:${server.port} #指定实例id
ribbon:
MaxAutoRetries: 2 #最大重试次数,当Eureka中可以找到服务,但是服务连不上时将会重试,如果eureka中找不到服务则直接走断路器
MaxAutoRetriesNextServer: 3 #切换实例的重试次数
OkToRetryOnAllOperations: false #对所有操作请求都进行重试,如果是get则可以,如果是post,put等操作没有实现幂等的情况下是很危险的,所以设置为false
ConnectTimeout: 5000 #请求连接的超时时间
ReadTimeout: 6000 #请求处理的超时时间

2.3消费者配置

引入jar包,这里需引入阿里云通信多的jar包和Redis的jar包

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--阿里云通信-->
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-core</artifactId>
<version>4.4.0</version>
<artifactId>aliyun-java-sdk-dysmsapi</artifactId>
<version>1.0.0</version>
<!-- 导入Eureka客户端的依赖 -->
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
<!-- feign相关依赖 -->
<artifactId>spring-cloud-starter-openfeign</artifactId>
<dependency>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<artifactId>spring-boot-starter-amqp</artifactId>

2.3.1 消费者配置类(同生产者)

package com.cui.sms.config;
import org.springframework.amqp.core.*;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/** rabbitmq配置类 配置交换机,消息队列,并且绑定交换机和queue
* @Author Cui
* @Date 2020-4-9 14:55
*/
@Configuration
public class RabbitmqConfig {
//队列bean的名称 cms 用来发送短信验证码
public static final String QUEUE_INFORM_CMS= "queue_inform_cms";
//队列bean的名称 email 用来发送邮件
//public static final String QUEUE_INFORM_EMAIL= "queue_inform_email";
//交换机的名称
public static final String EXCHANGE_TOPIC_INFORM_="exchange_topic_inform";
//队列的名称
@Value("${cxp.mq.queue}")
public String queue_cms_postpage_name;
//routingKey
@Value("${cxp.mq.routingKey}")
public String routingKey;
/**
* 交换机配置使用direct类型
* @return the exchange
*/
@Bean(EXCHANGE_TOPIC_INFORM_)
public Exchange EXCHANGE_TOPICS_INFORM() {
//durable(true) 持久化,mq重启之后交换机还在
return ExchangeBuilder.directExchange(EXCHANGE_TOPIC_INFORM_).durable(true).build();
}
//声明队列
@Bean(QUEUE_INFORM_CMS)
public Queue QUEUE_CMS_POSTPAGE() {
Queue queue = new Queue(QUEUE_INFORM_CMS);
return queue;
* 绑定队列到交换机
*
* @param queue the queue
* @param exchange the exchange
* @return the binding
@Bean
public Binding BINDING_QUEUE_INFORM_SMS(@Qualifier(QUEUE_INFORM_CMS) Queue queue, @Qualifier(EXCHANGE_TOPIC_INFORM_) Exchange exchange) {
return BindingBuilder.bind(queue).to(exchange).with(routingKey).noargs();
}

2.3.2 application.yml文件配置

server:
port: 8103
spring:
application:
name: cxp-manager-service-sms
rabbitmq:
host: 127.0.0.1
port: 5672
username: guest
password: guest
virtualHost: /
#Redis配置
redis:
port: 6379
password: 123456
jedis:
pool:
max-active: 8
max-wait: -1
max-idle: 500
min-idle: 0
lettuce:
shutdown-timeout: 0
aliyun:
sms:
accessKeyId: XXXXXXXXXXXXXXXXXXXX
accessKeySecret: XXXXXXXXXXXXXXXXXXXX
template_code: XXXXXXXXXXX
sign_name: XXXX
cxp:
mq:
#cms客户端监控的队列名称(不同的客户端监控的队列不能重复)
queue: queue_inform_cms
routingKey: inform.sms #此routingKey用来监听信息
eureka:
client:
registerWithEureka: true #服务注册开关
fetchRegistry: true #服务发现开关
serviceUrl: #Eureka客户端与Eureka服务端进行交互的地址,多个中间用逗号分隔
defaultZone: ${EUREKA_SERVER:http://localhost:50101/eureka/,http://localhost:50102/eureka/}
instance:
prefer-ip-address: true #将自己的ip地址注册到Eureka服务中
ip-address: ${IP_ADDRESS:127.0.0.1}
instance-id: ${spring.application.name}:${server.port} #指定实例id
ribbon:
MaxAutoRetries: 2 #最大重试次数,当Eureka中可以找到服务,但是服务连不上时将会重试,如果eureka中找不到服务则直接走断路器
MaxAutoRetriesNextServer: 3 #切换实例的重试次数
OkToRetryOnAllOperations: false #对所有操作请求都进行重试,如果是get则可以,如果是post,put等操作没有实现幂等的情况下是很危险的,所以设置为false
ConnectTimeout: 5000 #请求连接的超时时间
ReadTimeout: 6000 #请求处理的超时时间

3.写发送短信验证码的代码

3.1写一个controller来调用发送验证码的接口

/**
* 发送短信验证码
* @param phone
* @return
*/
@ApiOperation(value = "发送短信验证码",notes = "发送短信验证码")
@GetMapping("/sendSms")
public ResponseResult sendSms(String phone){
LOGGER.info("要发送的手机号为:{}", phone);
userService.sendSms(phone);
return new ResponseResult(UserMsg.SUCCESS.getMsgCd(), UserMsg.SUCCESS.getMsgInfo());
}

3.2 生成验证码

后台生成六位数的随机验证码,并且将验证码存入Redis中,设置五分钟的过期时间(用于用户注册时的校对),将验证码存到RabbitMQ中,当调用发送接口时,生产端将信息发送到绑定的队列中。

/**
* 向注册用户发送发送验证码
* @param phone 注册的用户的手机号
*/
@Override
public void sendSms(String phone) {
//1.生成六位随机验证码
Random random = new Random();//随机函数
int code = random.nextInt(999999);//设置随机数的最大值
if(code<100000){ //如果验证码小于六位数,加100000保证验证码为6位数
code+=100000;
}
//System.out.println("短信验证码:"+code);
LOGGER.info("生成的短信验证码为:{{}}", code);
//2.将验证码存入redis
redisTemplate.boundValueOps("code_"+phone).set(code+"");
redisTemplate.boundValueOps("code_"+phone).expire(5, TimeUnit.MINUTES);//设置验证码五分钟到期
//3.将验证码存入RabbitMQ
Map<String,String> map = new HashMap<String, String>();
map.put("phone", phone);
map.put("code", code+"");
//以json格式存到RabbitMQ消息队列中
rabbitTemplate.convertAndSend(EXCHANGE_TOPIC_INFORM_, routingKey, JSON.toJSONString(map));
}

3.3发送短信验证码

在RabbitMQ的消费者端监听短信的routingKey ,当收到生产端发来的消息后,便会调用阿里云通信向用户发送短信

package com.cui.sms.mq;
import com.alibaba.fastjson.JSON;
import com.aliyuncs.CommonResponse;
import com.cui.sms.utils.SmsUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.Message;
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 Cui
* @Date 2020-4-9 15:40
* 监听MQ,发送短信验证码
*/
@Component
public class SmsMessageConsumer {
private static final Logger LOGGER = LoggerFactory.getLogger(SmsMessageConsumer.class);
@Autowired
private SmsUtil smsUtil;
@Value("${aliyun.sms.template_code}")
private String templateCode;
@Value("${aliyun.sms.param}")
private String param; //短信参数
@RabbitListener(queues = {"${cxp.mq.queue}"})
public void onMessage(Message message) {
String jsonString= new String(message.getBody());//得到mq中存入的json格式的消息
Map<String,String> map = JSON.parseObject(jsonString, Map.class);//将json格式转换为Map格式
String phone = map.get("phone");//mq中存入的手机号
String code = map.get("code");//mq中存入的验证码
//System.out.println("手机号"+phone+"验证码"+code);
LOGGER.info("发送的手机号为:{} ,发送的验证码为 :{}",phone, code);
//调用阿里云通信
CommonResponse commonResponse = smsUtil.sendSms(phone, templateCode, param.replace("[value]", code));
}
}

3.4 实现验证码的校对

用户收到验证码并且填写完相应的信息后,点击注册,将自己的信息发送到后台,后台收到信息后,取出存在Redis中的验证码,和用户的验证码进行比较,然后将结果返回给前端。代码如下所示:

@PostMapping("/save")
@ApiOperation(value = "新增用户",notes = "新增用户")
public ResponseResult add(@RequestBody User user, String smsCode){
LOGGER.info("新增的用户的信息为:{},用户收到的验证码为:{}", user.toString(),smsCode);
//对用户密码进行加密后在存入数据库
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
String newPassword = encoder.encode(user.getPassword());
user.setPassword(newPassword);
userService.add(user,smsCode );
return new ResponseResult(UserMsg.SUCCESS.getMsgCd(), UserMsg.SUCCESS.getMsgInfo());
}
/**
* 用户注册
* @param user 用户对象信息
* @param smsCode 短信验证码
*/
@Override
public void add(User user, String smsCode) {
//获取系统验证码
String sysCode = (String) redisTemplate.boundValueOps("code_" + user.getPhone()).get();
//比较短信验证码
LOGGER.info("从Redis中取到的短信验证码为:{{}}",smsCode+" 用户收到的的短信验证码为:{{}}",smsCode);
if(sysCode==null||"".equals(smsCode)){
throw new RuntimeException("验证码未发送或已过期!请稍后重试");
}
if(!smsCode.equals(sysCode)){
throw new RuntimeException("验证码不正确,请重新输入!");
}
if(user.getUsername()==null){
user.setUsername(user.getPhone());
}
User searchUser = new User();
//将用户传来的手机号传给searchUser,去查询数据库中是否存在该手机号
searchUser.setPhone(user.getPhone());
if(userDao.selectCount(searchUser)>0){
throw new RuntimeException("该手机号已被注册!");
}
//设置user的其他参数
user.setCreated(new Date());
user.setUpdated(new Date());
user.setPoints(0);//积分初始值为0
user.setStatus("1");//状态1
user.setIsEmailCheck("0");//邮箱认证
user.setIsMobileCheck("1");//手机认证
userDao.insert(user);
}

到此这篇关于Springboot整合RabbitMQ实现发送验证码的功能的文章就介绍到这了,更多相关Springboot整合RabbitMQ发送验证码内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

    • 这篇文章主要给大家介绍了关于Spring Boot整合MyBatis连接Oracle数据库的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
      2018-07-07
    • 这篇文章主要介绍了SpringBoot配置数据库密码加密的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
      2021-03-03
    • 这是一个使用了java+Springboot+Maven+mybatis+Vue+mysql+wd开发的食品溯源系统,是一个毕业设计的实战练习,具有食品溯源该有的所有功能,感兴趣的朋友快来看看吧
      2022-01-01
    • 这篇文章主要介绍了Mybatis resultMap标签继承、复用、嵌套方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
      2022-03-03
    • 这篇文章主要介绍了Scala之文件读取、写入、控制台操作的方法示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
      2019-06-06
    • 这篇文章主要介绍了Java 多线程优先级实例详解的相关资料,需要的朋友可以参考下
      2017-04-04
    • 这篇文章主要介绍了java网络编程基础知识介绍,涉及OSI分层模型和TCP/IP分层模型的对应关系、IP地址、端口号、tcp、udp等相关内容,还是比较不错的,这里分享给大家,供需要的朋友参考。
      2017-11-11
    • 这篇文章主要介绍了Java方法参数传递机制原理解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
      2019-12-12
    • 这篇文章主要介绍了Java中的do...while循环语句的使用方法,是Java入门学习中的基础知识,需要的朋友可以参考下
      2015-10-10
    • 最近没有事情做,闲的发呆,于是写个东东练练手。这篇文章主要介绍了Java 多用户登录限制的实现方法的相关资料,需要的朋友可以参考下
      2016-11-11

    最新评论