Java项目:Java脚手架项目的阿里云短信服务集成(十六)

文章目录

  • 前言
  • 一、开通阿里云短信服务
  • 二、集成到项目里
    • [2.1 在 common 包下创建 common-message 包](#2.1 在 common 包下创建 common-message 包)
    • [2.2 添加 config 和 service 包](#2.2 添加 config 和 service 包)
    • [2.3 添加阿里云短信服务的配置在 config 里](#2.3 添加阿里云短信服务的配置在 config 里)
    • [2.4 在 service 下创建短信服务和验证码服务](#2.4 在 service 下创建短信服务和验证码服务)
    • [2.5 在 resources 下创建自动配置](#2.5 在 resources 下创建自动配置)
  • [三、配置 nacos](#三、配置 nacos)
  • END

鸡汤:
● 像手机充电:掉到1%才发现原来能撑那么久。累到极限时,你会惊讶自己还能笑着走两步。
● 下雨不是天空的错,但带不带伞是你的选择。阴天不会永远持续,但晒太阳的心情可以自己提前准备。

前言

前面实现了用户服务,现在我们集成一下阿里云的短信服务,因为用户在注册的时候是会发短信通知的

一、开通阿里云短信服务





因为现在短信服务不支持"个人资质"的签名实名制报备
所以我们搞短信认证服务



学习用可以小买 5 块的

添加测试手机号,进行测试

这里文档给的很全

二、集成到项目里

2.1 在 common 包下创建 common-message 包

2.2 添加 config 和 service 包

2.3 添加阿里云短信服务的配置在 config 里


AliSmsConfig:

java 复制代码
package com.my.commonmessage.config;

import com.aliyun.dypnsapi20170525.Client;
import com.aliyun.teaopenapi.models.Config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * 阿里云短信服务配置参数
 */
@Configuration
@RefreshScope
public class AliSmsConfig {

    /**
     * accessKeyId
     */
    @Value("${sms.aliyun.accessKeyId:}")
    private String accessKeyId;

    /**
     * accessKeySecret
     */
    @Value("${sms.aliyun.accessKeySecret:}")
    private String accessKeySecret;

    /**
     * 服务器地址
     */
    @Value("${sms.aliyun.endpoint:}")
    private String endpoint;

    /**
     * 注册客户端
     * @return 发送短信请求的客户端
     * @throws Exception
     */
    @Bean("aliClient")
    public Client createClient() throws Exception {

        com.aliyun.credentials.Client credential = new com.aliyun.credentials.Client();
        Config config = new Config().setCredential(credential);
        config.setEndpoint(endpoint);
        config.setAccessKeyId(accessKeyId);
        config.setAccessKeySecret(accessKeySecret);
        return new Client(config);
    }
}

2.4 在 service 下创建短信服务和验证码服务


AliSmsService:

java 复制代码
package com.my.commonmessage.service;

import com.aliyun.dypnsapi20170525.Client;
import com.aliyun.dypnsapi20170525.models.SendSmsVerifyCodeRequest;
import com.aliyun.dypnsapi20170525.models.SendSmsVerifyCodeResponse;
import com.aliyun.dypnsapi20170525.models.SendSmsVerifyCodeResponseBody;
import com.aliyun.teautil.models.RuntimeOptions;
import com.google.gson.Gson;
import com.my.commoncore.utils.JsonUtil;
import com.my.commondomain.constants.MessageConstants;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Map;

/**
 * 短信服务
 */
@Component
@RefreshScope
@Slf4j
public class AliSmsService {

    /**
     * 客户端
     */
    @Autowired
    private Client client;

    /**
     * 短信模版代码
     */
    @Value("${sms.aliyun.templateCode:}")
    private String templateCode;

    /**
     * 签名
     */
    @Value("${sms.sing-name:}")
    private String singName;

    /**
     * 是否发送线上短信
     */
    @Value("${sms.send-message:true}")
    private boolean sendMessage;

    /**
     *  验证码超时时间
     */
    @Value("${sms.code-expiration:5}")
    private String codeTimeout;

    /**
     * 发送短信验证码
     * @param phone 手机号
     * @param code 验证码
     * @return 是否发送成功
     */
    public boolean sendMobileCode(String phone, String code){
        // 封装一下
        Map<String, String> params = new HashMap<String, String>();
        params.put("code", code);
        params.put("min", codeTimeout);
        return sendTemMessage(phone,templateCode,params);
    }


    /**
     * 发送模版短信
     * @param phone 手机号
     * @param templateCode 模版编码
     * @param params 参数
     * @return 是否发送成功
     */
    public boolean sendTemMessage(String phone, String templateCode, Map<String, String> params){
        // 1. 判断是否打开短信通道
        if(!sendMessage){
            log.error("短信发送通道关闭, {}", phone);
            return false;
        }

        // 2. 创建请求
        SendSmsVerifyCodeRequest request = new SendSmsVerifyCodeRequest()
                .setSignName(singName)
                .setTemplateCode(templateCode)
                .setPhoneNumber(phone)
                .setTemplateParam(JsonUtil.toJson(params));

        RuntimeOptions runtimeOptions = new RuntimeOptions();
        // 3 发送请求,根据结果判断是否发送成功
        try {
            SendSmsVerifyCodeResponse resp = client.sendSmsVerifyCodeWithOptions(request,runtimeOptions);
            SendSmsVerifyCodeResponseBody body = resp.getBody();
            if(!body.getCode().equals(MessageConstants.SMS_MSG_OK)){
                log.error("短信{} 发送失败, 失败原因{}...", new Gson().toJson(request), body.getMessage());
                return false;
            }
            return true;
        } catch (Exception e) {
            log.error("短信{} 发送失败, 失败原因{}...", new Gson().toJson(request), e.getMessage());
            return false;
        }
    }
}

CaptchaService:

java 复制代码
package com.my.commonmessage.service;

import com.my.commoncore.utils.VerifyUtil;
import com.my.commondomain.constants.MessageConstants;
import com.my.commondomain.domain.ResultCode;
import com.my.commondomain.exception.ServiceException;
import com.my.commonredis.service.RedisService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.concurrent.TimeUnit;

/**
 * 验证码服务
 */
@Service
@RefreshScope
public class CaptchaService {

    /**
     *  阿里短信服务
     */
    @Autowired
    private AliSmsService aliSmsService;

    /**
     *  redis 缓存服务
     */
    @Autowired
    private RedisService redisService;

    /**
     * 单个手机号,每日发送短信次数的限制
     */
    @Value("${sms.send-limit:}")
    private Integer sendLimit;

    /**
     * 验证码的有效期,单位是分钟
     */
    @Value("${sms.code-expiration:}")
    private Long phoneCodeExpiration;

    /**
     * 用来判断是否发送随机验证码
     */
    @Value("${sms.send-message:true}")
    private boolean sendMessage;


    /**
     * 发送验证码
     * @param phone 手机号
     * @return 验证码
     */
    public String sendCode(String phone){
        // 1. 校验是否超过每日的发送限制(针对每个手机号)
        String limitCacheKey = MessageConstants.SMS_CODE_LIMIT_KEY + phone;
        Integer times = redisService.getCacheObject(limitCacheKey, Integer.class);
        times = times == null ? 0 : times;
        if(times >= sendLimit){
            // 抛出异常
            throw new ServiceException(ResultCode.SEND_MSG_OVERLIMIT);
        }

        // 2. 校验是否在1分钟内频繁发送
        String CacheKey = MessageConstants.SMS_CODE_KEY + phone;
        String code = redisService.getCacheObject(CacheKey, String.class);
        long expire = redisService.getExpire(CacheKey);
            // code 不空且 expire小于4分钟
        if(StringUtils.isNotBlank(code) && expire > phoneCodeExpiration * 60 - 60){
            long time = expire - phoneCodeExpiration * 60 - 60;
            throw new ServiceException("操作频繁, 请在"+ time+ "秒之后再试", ResultCode.INVALID_PARA.getCode());
        }

        // 3. 生成验证码
        String verifyCode = sendMessage ?
                VerifyUtil.generateVerifyCode(MessageConstants.DEFAULT_SMS_LENGTH)
                : MessageConstants.DEFAULT_SMS_CODE;

        // 4. 发送线上短信
        if(sendMessage) {
            boolean result  = aliSmsService.sendMobileCode(phone, verifyCode);
            if(!result){
                throw new ServiceException(ResultCode.SEND_MSG_FAILED);
            }
        }

        // 5. 设置验证码的缓存
        redisService.setCacheObject(CacheKey,verifyCode,phoneCodeExpiration, TimeUnit.MINUTES);
        // 6. 设置每日的发送限制缓存(无法预先设置缓存,只能先读后写)
        long seconds = ChronoUnit.SECONDS.between(LocalDateTime.now(),
                LocalDateTime.now().plusDays(1).withHour(0).withMinute(0).withSecond(0).withNano(0));

        redisService.setCacheObject(limitCacheKey,times + 1,seconds,TimeUnit.SECONDS);
        // 7. 返回验证码
        return verifyCode;
    }


    /**
     * 从缓存中删除手机号的验证码
     * @param phone 手机号
     * @return 验证码
     */
    public boolean delCode(String phone){
        String CacheKey = MessageConstants.SMS_CODE_KEY + phone;
        return redisService.deleteObject(CacheKey);
    }

    /**
     * 从缓存中获取手机号的验证码
     * @param phone 手机号
     * @return 验证码
     */
    public String getCode(String phone){
        String CacheKey = MessageConstants.SMS_CODE_KEY + phone;
        return redisService.getCacheObject(CacheKey,String.class);
    }

    /**
     * 校验手机号与验证码是否匹配
     * @param phone 手机号
     * @param code 验证码
     * @return 布尔类型
     */
    public boolean checkCode(String phone, String code){
        String cacheCode = getCode(phone);
        if(cacheCode == null || StringUtils.isBlank(cacheCode)){
            throw new ServiceException(ResultCode.INVALID_CODE);
        }
        return cacheCode.equals(code);
    }
}

2.5 在 resources 下创建自动配置

自动配置目录 : META.spring.org.springframework.boot.autoconfigure.AutoConfiguration.imports

将他们三个全部交给 Spring 容器管理

三、配置 nacos



share-sms-dev.yaml:

yaml 复制代码
sms:
  send-message: false
  send-limit: 200
  code-expiration: 5
  sing-name: "速通互联验证服务"
  aliyun:
    accessKeyId: 自己的accessKeyId
    accessKeySecret: 自己的accessKeySecret
    endpoint: dysmsapi.aliyuncs.com
    templateCode: 100001

END

项目代码实现完毕

相关推荐
寒秋花开曾相惜1 小时前
(学习笔记)2.2 整数表示(2.2.3 补码编码)
c语言·开发语言·笔记·学习
海南java第二人1 小时前
Flink运行时组件深度解析:Java工程师的架构设计与实战指南
java·大数据·flink
Jinkxs1 小时前
RabbitMQ - 第一个 Hello World 程序:SpringBoot 版极简集成
spring boot·rabbitmq·java-rabbitmq
WJX_KOI1 小时前
保姆级教程:Apache Flink CDC(standalone 模式)部署 MySQL CDC、PostgreSQL CDC 及使用方法
java·大数据·mysql·postgresql·flink
追随者永远是胜利者1 小时前
(LeetCode-Hot100)49. 字母异位词分组
java·算法·leetcode·职场和发展·go
CappuccinoRose1 小时前
CSS 语法学习文档(十七)
前端·css·学习·布局·houdini·瀑布流布局·csspaintingapi
吴声子夜歌1 小时前
RxJava——Flowable与背压
android·java·rxjava
Thanwind2 小时前
大二上结束随笔
java
啊阿狸不会拉杆2 小时前
《计算机视觉:模型、学习和推理》第 1 章 - 绪论
人工智能·python·学习·算法·机器学习·计算机视觉·模型