Spring Boot(十一):整合RabbitMQ消息中间件与阿里云发送手机验证码

上篇博文具体讲了RabbitMQ的知识,这篇博文就实际应用一下RabbitMQ,并借助阿里云进行发送短信验证码的功能。

注册阿里云短信服务

如果要使用阿里云的短信服务,就需要在阿里云的官网上申请。
首先登录阿里云,支付宝或者淘宝登录即可。
Spring Boot(十一):整合RabbitMQ消息中间件与阿里云发送手机验证码_第1张图片
登录完就是上面这个界面,然后在产品与服务中找到云通信下面的短信服务。
Spring Boot(十一):整合RabbitMQ消息中间件与阿里云发送手机验证码_第2张图片
如果没有开通,直接点击开通,因为博主已经开通了,所以进来就是这个界面。
Spring Boot(十一):整合RabbitMQ消息中间件与阿里云发送手机验证码_第3张图片
框框中的东西"签名管理",“模板管理”,需要根据自己的情况自己申请的,然后等系统通过即可。

当然,阿里云还提供了发送短信的代码Demo,这个读者可以自行查阅。

代码实现

需求:用户通过手机号注册,需要手机发送验证码。填写正确的验证码,就可以完成注册。
此项目还是基于另一篇博文:《SpringBoot中使用RabbitMQ消息中间件》
因为这里还是要使用消息中间件,所以,先导入Maven依赖。

        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-amqpartifactId>
        dependency>
        <dependency>
            <groupId>com.aliyungroupId>
            <artifactId>aliyun-java-sdk-coreartifactId>
            <version>4.1.0version>
        dependency>

要用到阿里云,所以也要将依赖导入。

然后修改配置文件。

spring:
  # 配置rabbitmq
  rabbitmq:
    host: 127.0.0.1
    port: 5672
    username: guest
    password: guest


# 阿里云相关配置
aliyun:
  sms:
    accessKeyId: 
    accessKeySecret: 
    signName1: 
    templateCode1: 

上面是RabbitMQ的配置,下面是阿里云的配置,accessKeyId和accessKeySecret如图所示。
Spring Boot(十一):整合RabbitMQ消息中间件与阿里云发送手机验证码_第4张图片
通过这里点进去就能看到。下面两个自定义配置就是刚才需要申请的"签名管理",“模板管理”,只有申请通过之后,才能使用。

创建生产者,发送短信验证码。
因为要与数据库交互,博主这里使用了MySQL数据库,使用JPA来操作,同时这里还使用了Redis。
如果使用其他数据库或者其他ORM框架的读者,需要使用博主的例子修改的话,也没有什么影响!

创建一个实体类,User

@Data
@Entity
@Table(name = "qf_user")
@ApiModel(value = "User", description = "用户")
public class User implements Serializable {

    @Id
    @ApiModelProperty(value = "主键")
    private String id;

    @ApiModelProperty(value = "手机")
    private String mobile;

    @ApiModelProperty(value = "密码")
    private String password;

    @ApiModelProperty(value = "盐值")
    private String salt;

    @Column(name = "nick_name")
    @ApiModelProperty(value = "昵称")
    private String nickName;

    @ApiModelProperty(value = "性别")
    private String sex;

    @ApiModelProperty(value = "生日")
    private Date birthday;

    @ApiModelProperty(value = "头像")
    private String avatar;

    @ApiModelProperty(value = "邮箱")
    private String email;

    @Column(name = "reg_date")
    @ApiModelProperty(value = "注册日期")
    private Date regDate;

    @Column(name = "update_date")
    @ApiModelProperty(value = "修改日期")
    private Date updateDate;

    @Column(name = "last_date")
    @ApiModelProperty(value = "最后登录日期")
    private Date lastDate;
}

因为这是博主项目中的一部分,为了方便,我就直接拷贝过来了。
关于dao层的部分就不上代码了,就一个增删改查而已。下面是service层

@Service
@Slf4j
@Transactional(rollbackFor = Exception.class)
public class UserService {

    @Autowired
    private UserRepository userRepository;

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Value("${spring.redis.key.mobile}")
    private String verifyCodeKey;

    public void save(User user) {
        user.setRegDate(new Date());
        user.setUpdateDate(new Date());
        user.setLastDate(new Date());
        userRepository.save(user);
    }

    /**
     *发送短信
     */
    public void sendMobileMessage(String mobile) {
        //生成六位随机数
        String verifyCode = RandomStringUtils.randomNumeric(6);
        //写入redis缓存
        redisTemplate.opsForValue().set(verifyCodeKey + mobile, verifyCode, 5, TimeUnit.MINUTES);
        //发给用户
        Map<String, String> stringMap = Maps.newHashMap();
        stringMap.put("mobile", mobile);
        stringMap.put("verifyCode", verifyCode);
        //使用了direct模式
        rabbitTemplate.convertAndSend(RabbitConstant.MOBILE_ROUTING_KEY, stringMap);
        //记录到日志中
        boolean enabled = log.isDebugEnabled();
        if (enabled) {
            log.debug("mobile:[{}],verifyCode:[{}]", mobile, verifyCode);
        }
    }

    /**
     *注册
     */
    public ResultBean register(User user, String code) {
        //判断redis中是否存在
        String verifyCode = redisTemplate.opsForValue().get(verifyCodeKey + user.getMobile());
        if (StringUtils.isEmpty(verifyCode)) {
            return new ResultBean(new CustomerException("短信验证码为空"), -1);
        }
        if (!verifyCode.equals(code)) {
            return new ResultBean(new CustomerException("验证码错误!"), 100);
        }
        save(user);
        return new ResultBean();
    }
}

里面有很多变量,博主都提取出来了,不可能将所有代码都放入,如果不明白的读者,可以给博主留言,博主一定会回答。

基本上生产者就完成了。执行上面的代码,就将验证码放入Redis缓存中,将手机号和验证码封装成Map放入消息队列中,可以作为一个伪发送短信的功能,只有等到发送短息的服务中的消费者来消费队列中的消息时,才能真正的发送验证码。

接着,将创建消息的消费者。实际上,这里需要提取出另一个服务,专门发送手机短信的服务。博主这里将其提取出来并封装了发送短信的工具类。

/**
 * @ClassName: SmsUtil
 * @Author: 清风一阵吹我心
 * @Description: TODO 阿里云发送短信验证码的工具类
 * @Date: 2019/10/26 13:37
 * @Version 1.0
 **/
@Component
public class SmsUtil {

    private static final String regionId = "cn-hangzhou";

    /**
     * 产品域名
     */
    private static final String DOMAIN = "dysmsapi.aliyuncs.com";
    /**
     * 系统规定参数。取值:SendSms
     */
    private static final String ACTION = "SendSms";

    /**
     * 可以得到当前配置文件中所有的信息
     */
    @Autowired
    private Environment environment;

    /**
     * //TODO
     * 发送短信
     *
     * @param phoneNumbers  手机号
     * @param signName      短信签名名称
     * @param templateCode  短信模板ID
     * @param templateParam 参数
     * @return com.aliyuncs.CommonResponse
     */
    public CommonResponse sendSms(String phoneNumbers, String signName, String templateCode, String templateParam) {
        String accessKeyId = environment.getProperty("aliyun.sms.accessKeyId");
        String accessKeySecret = environment.getProperty("aliyun.sms.accessKeySecret");
        //调整超时时间
        System.setProperty("sun.net.client.defaultConnectTimeout", "5000");
        System.setProperty("sun.net.client.defaultReadTimeout", "5000");

        DefaultProfile profile = DefaultProfile.getProfile(regionId, accessKeyId, accessKeySecret);
        IAcsClient client = new DefaultAcsClient(profile);

        CommonRequest request = new CommonRequest();
        request.setMethod(MethodType.POST);
        request.setDomain(DOMAIN);
        request.setVersion("2017-05-25");
        request.setAction(ACTION);
        //待发送手机号
        request.putQueryParameter("PhoneNumbers", phoneNumbers);
        //短信签名
        request.putQueryParameter("SignName", signName);
        //短信模板Id
        request.putQueryParameter("TemplateCode", templateCode);
        //短信模板变量对应的实际值,JSON格式。如果JSON中需要带换行符,请参照标准的JSON协议处理。
        request.putQueryParameter("TemplateParam", templateParam);
        //上行短信扩展码,无特殊需要此字段的用户请忽略此字段。
        //request.putQueryParameter("SmsUpExtendCode", "0000");
        //外部流水扩展字段
        //request.putQueryParameter("OutId", "0000");

        CommonResponse commonResponse = null;
        try {
            commonResponse = client.getCommonResponse(request);
        } catch (ClientException e) {
            e.printStackTrace();
        }
        return commonResponse;
    }
}

上面的代码在阿里云提供的文档中都有详细介绍,博主这里只是做了简单的修改。
发送短信的工具类完成后,需要用到RabbitMQ了,将消息队列中的消息消费掉,发送真正的短信到用户的手机上。

@Slf4j
@Component
@RabbitListener(queues = RabbitConstant.MOBILE_ROUTING_KEY)
public class MobileMessageListener {

    @Autowired
    private SmsUtil smsUtil;

    @Value("${aliyun.sms.signName1}")
    private String signName1;

    @Value("${aliyun.sms.templateCode1}")
    private String templateCode1;

    @RabbitHandler
    public void messageHandler(Map<String, String> map) {
        String mobile = map.get("mobile");
        String verifyCode = map.get("verifyCode");
        boolean debugEnabled = log.isDebugEnabled();
        if (debugEnabled) {
            log.debug("手机号:[{}],验证码:[{}]", mobile, verifyCode);
        }
        String templateParam = "{" + "code" + ":" + verifyCode + "}";
        smsUtil.sendSms(mobile, signName1, templateCode1, templateParam);
    }
}

基本上发送短信的功能就完成了,下面就可以启动服务测试了。
Spring Boot(十一):整合RabbitMQ消息中间件与阿里云发送手机验证码_第5张图片

因为功能并不复杂,通过文档就能够实现的。博主这里只是做一个简单的讲解。

技术之外

昨天博主看腾讯视频,发现《烈火英雄》可以看了,以前本来想去电影院看的,但是都没时间去,主要是因为“一个人不想出去”,emmmm~ 然后博主就借来一个腾讯视频的会员,把电影看了一遍。基本上,全程都是揪着心看的。觉得这个电影拍的太好了。真的觉得"人名英雄"太伟大了。心中也与那句“此生无悔入华夏,来生愿在种花家”,产生了共鸣。

千言万语都没有看一遍来的实际,如果有读者读到这里,并且还没有看过《烈火英雄》的,博主强烈建议大家去看呢,但是,首先要做好心理准备啊。

努力不一定成功,但不努力一定不成功

你可能感兴趣的:(Spring,Boot系列)