Java短信验证码

news2024/12/23 7:33:23

        想利用java给用户发送短信的话,需要我们与电信、移动、联通三大巨头合作(其实还有广电,但是比较少用),让它帮你发信息,当然直接与它合作显然是不现实的,所以我们要借助第三方的短信平台来替我们发信息。比较有名的短信平台,比如阿里云、腾讯云.....等都可以。短信平台就相当于我们与三大运行商的中介。

1 短信模板

短信模板内容不是随便想写什么就写什么的,每个短信模板都是要由国家工信部进行审核的。

短信模板包含两部分:

  1. 短信签名:【XXXXX】
  2. 短信正文:您的注册短信验证码为:1234

下面是腾讯云给的标准:(短信模板标准)

1、短信签名

签名内容不能含有黄赌毒、宗教和党政等信息。

签名必须能辨别所属公司或个人或者业务信息,不支持中性化签名。例如:xx工作日记、xx的心情、xx语言开发、xx业务测试、xx学习教程、xx个人日记等。即使所申请签名内容与支持的签名类型的名称相同但为中性,亦不支持。

国内短信签名由【】和签名内容组成,格式为【签名内容】。签名内容要求长度为2 - 12个字,由中英文、数字组成,内容不包含【】。 建议国内短信签名内容尽量使用中文。

国际/港澳台短信签名由[]和签名内容组成,格式为[签名内容]。签名内容要求2 - 15个字,内容不包含[]。

2、短信正文

 还有很多细节,大家可以按需自行翻阅文档。

2 开发思路

参考文章:dogbin丶

发送短信是一个比较慢的过程,因为需要用到第三方服务(阿里云短信服务),因此我们使用RabbitMq来做异步处理,前端点击获取验证码后,后端做完校验限流后直接返回发送成功。

发送短信的服务是需要收费的,而且我们也不允许用户恶意刷接口,所以需要有一个接口限流方案。

3 具体实现

至于RabbitMq怎么用,我这边就不再讲解了,因为这部分内容也挺多的,当然不用rabbitmq这类中间件,也完全可以实现短信开发功能,但是不推荐哦,因为这可能导致用户体验不好。(大伙如果不会可以先学学,因为其实RabbitMq用处还是挺多的)。

maven依赖

         <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>com.aliyun</groupId>
            <artifactId>dysmsapi20170525</artifactId>
            <version>3.0.0</version>
        </dependency>

1、接收短信请求的接口

@RestController
@RequestMapping("/user")
public class UserController {
   
    @Autowired
    private RedisUtil redisUtil;
    @Autowired
    private RabbitTemplate rabbitTemplate;

@GetMapping("/getCode")
    public String getCode(String phone){
        String regex="^(13[0-9]|14[01456879]|15[0-35-9]|16[2567]|17[0-8]|18[0-9]|19[0-35-9])\\d{8}$";
//判断手机号是否合法
        if(Pattern.matches(regex,phone)){
            //判断该手机号一分钟内是否重复提交申请,请求记录保存在redis里面
            String key="SMS::request"+phone;
               //为空说明一分钟内没有请求
           if(redisUtil.getCacheObject(key)==null){
               //将请求记录保存在redisz中,redisUtil是一个工具类
               redisUtil.setCacheObject(key,"",60, TimeUnit.SECONDS);
               //交换机名
               String exchange="user.exchange"; 
                //发送到rabbit交换机中
               rabbitTemplate.convertAndSend(exchange,"",phone);
               return "验证码已发送";
           }else {
               return "一分钟内已申请过验证码,请稍后再申请";
           }
        }else {
            System.out.println("手机号格式不正确!");
            return "手机号格式不正确!";
        }

    }
}

 最终将短信放到rabbit里面等待处理。

user.exchange是一个rabbitmq的交换机

2 rabbit接收发送短信请求

@Component
public class PhoneCodeController {
    @Autowired
    private SMSUtils smsUtils;
    @RabbitListener(queues = "phoneCode")
    public void getCode(String phone){
        try {
            //随机生成4位数字验证码
            String code = (int)((Math.random()*9+1)*1000)+"";
            //发送验证码
            smsUtils.sendMessage(phone, "阿里云短信测试", "SMS_154950909", code);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

 @RabbitListener(queues = "phoneCode")监听交换机,发送给队列的信息

注意队列和交换机关系要绑定好。(不知道怎么绑定的,可以先学一下rabbitmq)

3 发送短信的工具类

@Component
public class SMSUtils {
    @Autowired
    private RedisUtil redisUtil;
    /**
     * 阿里云登录id
     */
    private final static String ACCESS_KEY_ID = "。。。。。。。。";
 
    /**
     * 阿里云登录密码
     */
    private final static String ACCESS_KEY_SECRET = "。。。。。。。。。";
 
    /**
     * 初始化登录阿里云的Client
     *
     * @param accessKeyId
     * @param accessKeySecret
     * @return Client
     */
    public static com.aliyun.dysmsapi20170525.Client createClient(String accessKeyId, String accessKeySecret) throws Exception {
        com.aliyun.teaopenapi.models.Config config = new com.aliyun.teaopenapi.models.Config()
        .setAccessKeyId(accessKeyId)
        .setAccessKeySecret(accessKeySecret);
        // 可指定登录的服务器地址,可参考 https://api.aliyun.com/product/Dysmsapi
        config.endpoint = "dysmsapi.aliyuncs.com";
        return new com.aliyun.dysmsapi20170525.Client(config);
    }
 
    /**
     * 发送短信方法
     * @param phoneNumbers 手机号
     * @param signName 签名
     * @param templateCode  模板编号
     * @param code 模板参数
     * @throws Exception
     */
    public  void sendMessage(String phoneNumbers, String signName, String templateCode, String code) throws Exception {
        System.out.println(11);
        //hashmap转化位json
        HashMap<String, String> map = new HashMap<>();
        map.put("code", code);
        String jsonCode = JSONUtil.toJsonStr(map);


        com.aliyun.dysmsapi20170525.Client client = SMSUtils.createClient(ACCESS_KEY_ID, ACCESS_KEY_SECRET);
        com.aliyun.dysmsapi20170525.models.SendSmsRequest sendSmsRequest = new com.aliyun.dysmsapi20170525.models.SendSmsRequest()
        .setPhoneNumbers(phoneNumbers)
        .setSignName(signName)
        .setTemplateCode(templateCode)
        .setTemplateParam(jsonCode);
        SendSmsResponse sendSmsResponse = client.sendSmsWithOptions(sendSmsRequest, new RuntimeOptions());

        if(sendSmsResponse.getBody().code.equals("OK")){
            System.out.println("短信发送成功");
           //短信验证码存储在redis,五分钟过期
            redisUtil.setCacheObject("phone::code"+phoneNumbers,code,5,TimeUnit.MINUTES);


        }else {
            System.out.println(sendSmsResponse.getBody().message);
            System.out.println("短信发送失败");
        }

    }
 

 
}

ACCESS_KEY_ID和ACCESS_KEY_SECRET需要更换为自己的哦,获取方式如下

点击创建

 

4 还有一些工具类如下

package org.example.common.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundSetOperations;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * spring redis 工具类
 *
 * @author ruoyi
 **/
@SuppressWarnings(value = {"unchecked", "rawtypes"})
@Component
public class RedisUtil {
    @Autowired
    public RedisTemplate redisTemplate;

    /**
     * 缓存基本的对象,Integer、String、实体类等
     *
     * @param key   缓存的键值
     * @param value 缓存的值
     */
    public <T> void setCacheObject(final String key, final T value) {
        redisTemplate.opsForValue().set(key, value);
    }

    /**
     * 缓存基本的对象,Integer、String、实体类等
     *
     * @param key      缓存的键值
     * @param value    缓存的值
     * @param timeout  时间
     * @param timeUnit 时间颗粒度
     */
    public <T> void setCacheObject(final String key, final T value, final Integer timeout, final TimeUnit timeUnit) {
        redisTemplate.opsForValue().set(key, value, timeout, timeUnit);
    }

    /**
     * 设置有效时间
     *
     * @param key     Redis键
     * @param timeout 超时时间
     * @return true=设置成功;false=设置失败
     */
    public boolean expire(final String key, final long timeout) {
        return expire(key, timeout, TimeUnit.SECONDS);
    }

    /**
     * 设置有效时间
     *
     * @param key     Redis键
     * @param timeout 超时时间
     * @param unit    时间单位
     * @return true=设置成功;false=设置失败
     */
    public boolean expire(final String key, final long timeout, final TimeUnit unit) {
        return redisTemplate.expire(key, timeout, unit);
    }

    /**
     * 获得缓存的基本对象。
     *
     * @param key 缓存键值
     * @return 缓存键值对应的数据
     */
    public <T> T getCacheObject(final String key) {
        ValueOperations<String, T> operation = redisTemplate.opsForValue();
        return operation.get(key);
    }

    /**
     * 删除单个对象
     *
     * @param key
     */
    public boolean deleteObject(final String key) {
        return redisTemplate.delete(key);
    }

    /**
     * 删除集合对象
     *
     * @param collection 多个对象
     * @return
     */
    public long deleteObject(final Collection collection) {
        return redisTemplate.delete(collection);
    }

    /**
     * 缓存List数据
     *
     * @param key      缓存的键值
     * @param dataList 待缓存的List数据
     * @return 缓存的对象
     */
    public <T> long setCacheList(final String key, final List<T> dataList) {
        Long count = redisTemplate.opsForList().rightPushAll(key, dataList);
        return count == null ? 0 : count;
    }

    /**
     * 获得缓存的list对象
     *
     * @param key 缓存的键值
     * @return 缓存键值对应的数据
     */
    public <T> List<T> getCacheList(final String key) {
        return redisTemplate.opsForList().range(key, 0, -1);
    }

    /**
     * 缓存Set
     *
     * @param key     缓存键值
     * @param dataSet 缓存的数据
     * @return 缓存数据的对象
     */
    public <T> BoundSetOperations<String, T> setCacheSet(final String key, final Set<T> dataSet) {
        BoundSetOperations<String, T> setOperation = redisTemplate.boundSetOps(key);
        Iterator<T> it = dataSet.iterator();
        while (it.hasNext()) {
            setOperation.add(it.next());
        }
        return setOperation;
    }

    /**
     * 获得缓存的set
     *
     * @param key
     * @return
     */
    public <T> Set<T> getCacheSet(final String key) {
        return redisTemplate.opsForSet().members(key);
    }

    /**
     * 缓存Map
     *
     * @param key
     * @param dataMap
     */
    public <T> void setCacheMap(final String key, final Map<String, T> dataMap) {
        if (dataMap != null) {
            redisTemplate.opsForHash().putAll(key, dataMap);
        }
    }

    /**
     * 获得缓存的Map
     *
     * @param key
     * @return
     */
    public <T> Map<String, T> getCacheMap(final String key) {
        return redisTemplate.opsForHash().entries(key);
    }

    /**
     * 往Hash中存入数据
     *
     * @param key   Redis键
     * @param hKey  Hash键
     * @param value 值
     */
    public <T> void setCacheMapValue(final String key, final String hKey, final T value) {
        redisTemplate.opsForHash().put(key, hKey, value);
    }

    /**
     * 获取Hash中的数据
     *
     * @param key  Redis键
     * @param hKey Hash键
     * @return Hash中的对象
     */
    public <T> T getCacheMapValue(final String key, final String hKey) {
        HashOperations<String, String, T> opsForHash = redisTemplate.opsForHash();
        return opsForHash.get(key, hKey);
    }

    /**
     * 删除Hash中的数据
     *
     * @param key
     * @param hKey
     */
    public void delCacheMapValue(final String key, final String hKey) {
        HashOperations hashOperations = redisTemplate.opsForHash();
        hashOperations.delete(key, hKey);
    }

    /**
     * 获取多个Hash中的数据
     *
     * @param key   Redis键
     * @param hKeys Hash键集合
     * @return Hash对象集合
     */
    public <T> List<T> getMultiCacheMapValue(final String key, final Collection<Object> hKeys) {
        return redisTemplate.opsForHash().multiGet(key, hKeys);
    }

    /**
     * 获得缓存的基本对象列表
     *
     * @param pattern 字符串前缀
     * @return 对象列表
     */
    public Collection<String> keys(final String pattern) {
        return redisTemplate.keys(pattern);
    }
}

 5 application.yml

server:
  port: 9005
spring:
  application:
    name: my-consumer
  rabbitmq:
    host: 47.122.62.29 # 你的虚拟机IP
    port: 5672 # 端口
    virtual-host: / # 虚拟主机
    username: lsw # 用户名
    password: 123456 # 密码

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2136235.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于Python的量化交易回测框架Backtrader初识记录(二)

版权声明&#xff1a;本文为博主原创文章&#xff0c;如需转载请贴上原博文链接&#xff1a;基于Python的量化交易回测框架Backtrader初识记录&#xff08;二&#xff09;-CSDN博客 前言&#xff1a;在上一篇文章 基于Python的量化交易回测框架Backtrader初识记录&#xff08;一…

加速开发体验:为 Android Studio 设置国内镜像源

Android Studio 是由 JetBrains 开发的一个官方 IDE&#xff0c;用于 Android 应用开发。由于网络原因&#xff0c;直接从 Google 的服务器下载可能会比较慢或者不稳定。幸运的是&#xff0c;我们可以通过配置国内镜像源来加速下载和更新。 文章目录 &#x1f4af; 修改 Gradle…

网络工程师学习笔记——网络互连与互联网

互联网的定义 由多个网络相互连接组成更大的网络称为互联网 常见的网络设备&#xff08;是网络拓扑结构和网络的基础&#xff09; 物理层 中继器&#xff08;是将传输的信号进行放大&#xff0c;延长传输的距离&#xff09;&#xff0c;集线器也是这样&#xff0c;但是有更多…

【828华为云征文|全面指南:使用华为云Flexus X部署私人图床】

文章目录 华为云Flexus X实例介绍图床功能介绍部署自己的图床准备工作服务器环境确认宝塔软件商店简单图床一键部署域名解析简单图床安装向导页安装环境检测网站基础配置 访问图床登录页使用图床功能验证效果 总结 华为云Flexus X实例介绍 华为云Flexus云服务是专为中小企业和…

Android系统dumpsys命令详解

文章目录 1. dumpsys 的工作原理2. 基本使用方法执行 dumpsys限制 dumpsys 的输出 3. 常见的 dumpsys 服务1. Activity Manager (activity)2. Battery Service (battery)3. Window Manager (window)4. Package Manager (package)5. Power Manager (power)6. Media DRM (media.d…

supermap icilent3d for cesium加载地形并夸大地形

先看效果图 这是没有夸张之前的都江堰 这是夸大五倍后的都江堰 下面展示代码 主要就是加载supermaponline的skt地形然后夸大 <template><div class"PartOneBox"><div id"cesiumContainer"></div></div> </template>…

【高等数学学习记录】函数

【高等数学&学习记录】函数 从事测绘工作多年&#xff0c;深刻感受到基础知识的重要及自身在这方面的短板。 为此&#xff0c;打算重温测绘工作所需基础知识。练好基本功&#xff0c;为测绘工作赋能。 1 知识点 1.1 函数 设数集 D ⊂ R D\subset R D⊂R&#xff0c;称映射…

MOS管和三极管有什么区别?

MOS管是基于金属-氧化物-半导体结构的场效应晶体管&#xff0c;它的控制电压作用于氧化物层&#xff0c;通过调节栅极电势来控制源漏电流。MOS管是FET中的一种&#xff0c;现主要用增强型MOS管&#xff0c;分为PMOS和NMOS。 MOS管的三个极分别是G(栅极)&#xff0c;D(漏极)&…

企业文件防泄密软件有哪些?|十款主流文件防泄密解决方案

在数据泄露事件频发的今天&#xff0c;企业对文件防泄密解决方案的需求日益增加。选择适合的文件防泄密工具对于保护敏感信息、维护企业声誉和合规性至关重要。本文将盘点十款主流文件防泄密解决方案&#xff0c;帮助企业了解各方案的特点和优势&#xff0c;从而做出明智的选择…

注解(Java程序的一种特殊“注释”,用于工具处理的标注)

1.使用注解 a.含义 i.注解是放在类&#xff0c;字段&#xff0c;方法&#xff0c;参数前的一种特殊“注释”。 ii.注释会被编译器直接忽略&#xff0c;注解则可以被编译器打包带进class文件&#xff0c;因此&#xff0c;注解是一种用于标注的“元数据”。 iii…

链路聚合(Link Aggregation)

链路聚合&#xff08;Link Aggregation&#xff09;&#xff0c;也被称为端口聚合&#xff08;Port Aggregation&#xff09;、捆绑&#xff08;Bonding&#xff09;或团队&#xff08;Teaming&#xff09;&#xff0c;是一种将多个网络接口结合成一个逻辑接口&#xff0c;以提…

网页模板该怎么选

选择网页模板是建立一个成功网站的关键步骤之一。一个合适的网页模板可以提高用户体验&#xff0c;提升网站的专业度&#xff0c;并使内容更易于阅读。在选择网页模板时&#xff0c;需要考虑多个因素&#xff0c;包括网站类型、目标受众、内容类型以及个人品味。以下是一些建议…

【时时三省】(C语言基础)指针进阶 例题7

山不在高&#xff0c;有仙则名。水不在深&#xff0c;有龙则灵。 ----CSDN 时时三省 二维数组 第一个a 因为它有12个元素 每个元素占4个字节 所以就打印48 第二个a&#xff3b;0&#xff3d;&#xff3b;0&#xff3d; 表示是第一行第一个元素 所…

35岁嫌老,65嫌年轻,程序员还有路子吗?

如今&#xff0c;延迟退休的概念越来越被人们所接受和认同。35岁嫌老&#xff0c;65嫌年轻成为了当下社会的新趋势。然而&#xff0c;对于那些本来就存在着35岁危机的程序员们来说&#xff0c;如何应对这一挑战&#xff0c;迎接新的职业生涯呢&#xff1f; 在这篇文章中&#…

出处不详 投篮

目录 投篮题目描述背景输入输出数据范围 题解解法 打赏 投篮 题目描述 背景 现在你得到了一个可以阻拦投篮的宝物&#xff0c;它会在投球后把篮球传送回运动员手上&#xff0c;但是宝物的成功率和篮球在空中运动的时间有关&#xff0c;并且在特定的时间点成功的几率是固定的…

大势智慧与山东省国土测绘院签署战略合作协议

9月6日&#xff0c;山东省国土测绘院&#xff08;后简称山东院&#xff09;与武汉大势智慧科技有限公司&#xff08;后简称大势智慧&#xff09;签署战略合作协议。 山东院院长田中原、卫星应用中心主任相恒茂、基础测绘中心主任魏国忠、卫星应用中心高级工程师张奇伟&#xf…

S32G EB tresos AutoCore下载和激活方法

文章目录 1. 下载1.1 EB tresos AutoCore下载1.2 EB激活工具&#xff08;EB_Client_License_Administrator&#xff09;下载 2 安装3 激活4 展示 本文将介绍EB tresos的AutoCore&#xff08;CP BSW配置工具&#xff09;和MCAL驱动安装包的下载、安装和激活方法。 更多AUTOSAR C…

lightdm , xrandr , startx 桌面管理器,窗口管理器

问题&#xff1a; 了解这几个的含义。 显示服务器 这个不是很明白 显示管理器&#xff0c; 知道就行了&#xff0c;也不是很明白。 窗口管理器。 桌面管理器。 这个其实就是 桌面环境了&#xff0c; 我们的板卡上使用的是xface 。 这个 xface 是一个集合&#xff0c;这里面…

JavaScript --函数的作用域(全局和局部)

全局作用域 全局作用域&#xff0c;就算不在一个script标签也能调用 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta nam…

计算左边(比自己小的元素)的最长距离

前言&#xff1a;一般做的题目都是使用单调栈来求出距离这个点最近的那个比这个数大或小的元素&#xff0c;但是如果是需要找到最远的那个元素呢&#xff1f;我们可以用到类似逆序对的思路&#xff0c;我们先进行排序从小到大&#xff0c;接着我们先处理左边&#xff0c;每次维…