spring boot3登录开发-2(2短信验证码接口实现)

 

⛰️个人主页:     蒾酒

🔥系列专栏:《spring boot实战》

🌊山高路远,行路漫漫,终有归途


目录

写在前面

上文衔接

内容简介

短信验证码接口实现

1.依赖导入

2.接口分析

3.实现思路

3.功能实现

创建发送短信工具类

配置阿里云短信服务

接口代码实现

4.功能测试

写在最后


写在前面

本文介绍了springboot开发后端服务中,短信验证码接口功能的设计与实现,坚持看完相信对你有帮助。

同时欢迎订阅springboot系列专栏,持续分享spring boot的使用经验。

上文衔接

本文衔接上文,可以看一下:

spring boot3登录开发-2(1图形验证码接口实现)_用户登录验证码接口设计-CSDN博客文章浏览阅读5.2k次,点赞146次,收藏109次。上文我们已经整合好了jwt,本文我们开始实现图形验证码接口的实现。通过糊涂工具包的图形验证码工具完成获取验证码接口通过redis缓存key(验证码id)-value(验证码内容)_用户登录验证码接口设计https://blog.csdn.net/qq_62262918/article/details/136064820?spm=1001.2014.3001.5502

内容简介

上文我们已经整合好了jwt,本文我们开始实现短信验证码接口的实现。这里我选用阿里云的短信服务。本文侧重点在于设计思路,阿里云控制台开通短信服务,你跟着流程走一遍就可以。个人也是可以申请的。

短信验证码接口实现

1.依赖导入

导入阿里云短信服务SDK

pom.xml:

 <dependency><groupId>com.aliyun</groupId><artifactId>dysmsapi20170525</artifactId><version>2.0.24</version></dependency>

2.接口分析

关键点:

  1. 60s发送间隔
  2. 连发多次封禁24H

3.实现思路

接口接收一个手机号参数,先去redis查询该手机是否有未过期的验证码,如果有先判断发送次数,超过5次,先修改过期时间为24H,再抛发送次数过多异常,少于五次,就再判断发送频率,距离上次发送间隔少于60s,就抛出发送频繁异常,  如果没有未过期的验证码,代表这是第一次发送就直接生成验证码发送短信,接着redis存入一个哈希,key通过手机号生成,value就是,三个键值对,验证码,上次发送时间戳,发送次数(初始为1),默认过期时间5分钟

  1. 接收手机号参数。
  2. 查询 Redis 中该手机号是否有未过期的验证码。
  3. 如果有未过期的验证码:
    • 判断发送次数是否超过5次,如果超过5次,修改过期时间为24小时并抛出发送次数过多异常。
    • 如果发送次数未超过5次,再判断距离上次发送时间间隔是否少于60秒,如果少于60秒,抛出发送频繁异常。
  4. 如果没有未过期的验证码,代表第一次发送:
    • 生成验证码并发送短信。
    • 将验证码信息存入 Redis,包括验证码、上次发送时间戳和发送次数(初始为1),设置过期时间为5分钟。

注意事项:

考虑到验证码本身的敏感性,虽然存入Redis是临时的,但考虑安全建议对验证码内容进行加密存储,增强数据的安全性。

考虑对接口调用方进行身份验证,确保只有合法的客户端可以请求发送验证码。

整合redis:

Spring Boot3整合Redis_springboot 3 集成redis-CSDN博客文章浏览阅读5.4k次,点赞101次,收藏105次。spring boot整合redis简单四步即可。_springboot 3 集成redishttps://blog.csdn.net/qq_62262918/article/details/136067550?spm=1001.2014.3001.5501

3.功能实现

创建发送短信工具类

import com.aliyun.dysmsapi20170525.Client;
import com.aliyun.dysmsapi20170525.models.SendSmsRequest;
import com.aliyun.dysmsapi20170525.models.SendSmsResponse;
import com.aliyun.teaopenapi.models.Config;
import com.aliyun.teautil.models.RuntimeOptions;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;/*** @author mijiupro*/
@Data
@Component
@ConfigurationProperties(prefix = "aliyun.sms")
@Slf4j
public class SmsUtil {private String accessKeyId;// 访问密钥idprivate String accessKeySecret;// 访问密钥secretprivate String endpoint;// 短信 API 的访问域名private String signName;// 短信签名private String templateCode;// 短信模板ID// 发送短信public Boolean sendSms(String phone, String code) {// 创建阿里云客户端Config config = new Config().setAccessKeyId(accessKeyId)// 配置访问密钥 ID.setAccessKeySecret(accessKeySecret);// 配置密钥config.endpoint = endpoint;// 配置访问端点Client client;try {client = new Client(config);} catch (Exception e) {log.error("阿里云短信服务初始化失败", e);return false;}// 构建发送请求SendSmsRequest sendSmsRequest = new SendSmsRequest().setSignName(signName) // 设置签名.setTemplateCode(templateCode) // 设置模板.setPhoneNumbers(phone) // 设置手机号为参数传入的值.setTemplateParam("{\"code\":\"" + code + "\"}"); // 设置模板参数为传入的验证码RuntimeOptions runtime = new RuntimeOptions();// 运行时选择,可以设置不同的属性来配置运行时环境的参数。try {// 复制代码运行请自行打印 API 的返回值SendSmsResponse sendSmsResponse = client.sendSmsWithOptions(sendSmsRequest, runtime);if (!"OK".equals(sendSmsResponse.getBody().getCode())) {log.error("短信发送失败:{}", sendSmsResponse.getBody().getMessage());return false;}log.info("短信发送成功");return true;} catch (Exception error) {log.error("短信发送失败:{}", error.getMessage());return false;}}
}

配置阿里云短信服务

application.yml:

此处修改为你的信息,这里我乱写的

aliyun:sms:access-key-id: Lih5disdjisdidaccess-key-secret: sdisajfdisf6s7d88sdendpoint: dysmsapi.aliyuncs.comsignName: mijiutemplateCode: SMS_46544097

接口代码实现

接口

public interface CaptchaService {/***  获取短信验证码* @param phone*/void getSmsCaptcha(String phone);}

实现类


import com.mijiu.commom.exception.GeneralBusinessException;
import com.mijiu.commom.util.SmsUtil;
import com.mijiu.service.CaptchaService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import java.util.concurrent.TimeUnit;/*** @author mijiupro*/
@Service
@Slf4j
public class CaptchaServiceImpl implements CaptchaService {private final StringRedisTemplate stringRedisTemplate;private final SmsUtil smsUtil;public CaptchaServiceImpl(StringRedisTemplate stringRedisTemplate, SmsUtil smsUtil) {this.stringRedisTemplate = stringRedisTemplate;this.smsUtil = smsUtil;}@Overridepublic void getSmsCaptcha(String phone) {String hashKey = "login:sms:captcha:" + phone;BoundHashOperations<String, String, String> hashOps = stringRedisTemplate.boundHashOps(hashKey);// 初始检查String lastSendTimestamp = hashOps.get("lastSendTimestamp");String sendCount = hashOps.get("sendCount");String captcha = hashOps.get("captcha");hashOps.expire(5, TimeUnit.MINUTES); // 设置过期时间为5分钟// 判断发送次数是否超过限制if (StringUtils.isNotBlank(sendCount) && Integer.parseInt(sendCount) >= 5) {hashOps.expire(24, TimeUnit.HOURS); // 重新设置过期时间为24Hthrow new GeneralBusinessException("发送次数过多,请24H后再试");}// 判断发送频率是否过高if (StringUtils.isNotBlank(lastSendTimestamp)) {long lastSendTime = Long.parseLong(lastSendTimestamp);long currentTime = System.currentTimeMillis();long elapsedTime = currentTime - lastSendTime;long interval = 60 * 1000; // 60秒if (elapsedTime < interval) {throw new GeneralBusinessException("发送短信过于频繁,请稍后再试");}}// 更新发送次数int newSendCount = StringUtils.isNotBlank(sendCount) ? Integer.parseInt(sendCount) + 1 : 1;// 生成新验证码if (StringUtils.isBlank(captcha)) {captcha = RandomStringUtils.randomNumeric(6);}// 发送短信if (!smsUtil.sendSms(phone, captcha)) {throw new GeneralBusinessException("发送短信失败");}// 更新 Redis 中的信息hashOps.put("captcha", captcha);hashOps.put("lastSendTimestamp", String.valueOf(System.currentTimeMillis()));hashOps.put("sendCount", String.valueOf(newSendCount));}}

控制器


import com.mijiu.service.CaptchaService;
import io.swagger.v3.oas.annotations.Operation;import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;/*** @author mijiupro*/
@RestController
@RequestMapping("/captcha")
@Tag(name = "验证码接口", description = "验证码接口相关操作")
public class CaptchaController {private final CaptchaService captchaService;public CaptchaController(CaptchaService captchaService) {this.captchaService = captchaService;}@GetMapping("/sms-captcha/{phone}")@Operation(summary = "获取短信验证码")public void getSmsCaptcha(@PathVariable String phone) {captchaService.getSmsCaptcha(phone);}
}

说明:

代码里面我直接返回void是因为我做了全局响应结果拦截统一封装,实际应用为了考虑防盗刷等因素并不会返回空。

4.功能测试

我这里是整合了swagger3的,相关文章在本专栏:

Spring Boot3整合knife4j(swagger3)_springboot3 knife4j-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/qq_62262918/article/details/135761392?spm=1001.2014.3001.5502

 直接发一次

 

 60内重复发送

连发5次

写在最后

对接阿里云短信服务实现短信验证码接口到这里就结束了,接口设计我尽可能简单并有一定安全性考虑。希望看完对你有帮助。后面我会出一篇仿百度云短信验证接口的设计实现(考虑更多防盗刷策略),欢迎大家订阅本专栏。任何问题评论区或私信讨论,欢迎指正。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

QT的学习

代码练习 做一个UI登录页面 源文件 #include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this);this->setWindowTitle("洛克启动&#xff01;");this->…

Cinema 4D 2024 for mac/Win:开启三维动画与建模新纪元

在数字化时代&#xff0c;三维动画与建模已成为影视、游戏、广告等多个领域不可或缺的创作工具。而Cinema 4D&#xff0c;作为这一领域的佼佼者&#xff0c;始终以其卓越的性能和创新的功能引领着行业的发展。如今&#xff0c;Cinema 4D 2024的发布&#xff0c;更是为我们带来了…

【数据结构与算法】(14):堆排序和选择排序(超详细配图~)

&#x1f921;博客主页&#xff1a;Code_文晓 &#x1f970;本文专栏&#xff1a;数据结构与算法 &#x1f63b;欢迎关注&#xff1a;感谢大家的点赞评论关注&#xff0c;祝您学有所成&#xff01; ✨✨&#x1f49c;&#x1f49b;想要学习更多数据结构与算法点击专栏链接查看&…

RuoYi-Vue开源项目2-前端登录验证码生成过程分析

前端登录验证码实现过程 生成过程分析 生成过程分析 验证码的生成过程简单概括为&#xff1a;前端登录页面加载时&#xff0c;向后端发送一个请求&#xff0c;返回验证码图片给前端页面展示 前端页面加载触发代码&#xff1a; import { getCodeImg } from "/api/login&q…

HarmonyOS(鸿蒙)ArkUI组件

方舟开发框架&#xff08;简称ArkUI&#xff09;为HarmonyOS应用的UI开发提供了完整的基础设施&#xff0c;包括简洁的UI语法、丰富的UI功能&#xff08;组件、布局、动画以及交互事件&#xff09;&#xff0c;以及实时界面预览工具等&#xff0c;可以支持开发者进行可视化界面…

jar读取目录配置、打包jar后无法获取目录下的配置

jar读取目录配置、打包jar后无法获取目录下的配置 jar读取目录配置、打包jar后无法获取目录下的配置。java打成jar包后获取不到配置文件路径。解决项目打成jar包上线无法读取配置文件。打包jar后无法读取resource下的配置文件 场景 需要读取 src/main/resources/mapper下的所…

Windows系统部署GoLand结合内网穿透实现SSH远程Linux服务器开发调试

&#x1f308;个人主页: Aileen_0v0 &#x1f525;热门专栏: 华为鸿蒙系统学习|计算机网络|数据结构与算法|MySQL| ​&#x1f4ab;个人格言:“没有罗马,那就自己创造罗马~” #mermaid-svg-HIOuHATnug3qMHzx {font-family:"trebuchet ms",verdana,arial,sans-serif;f…

es 聚合操作(二)

书接上文&#xff0c;示例数据在上一篇&#xff0c;这里就不展示了 一、Pipeline Aggregation 支持对聚合分析的结果&#xff0c;再次进行聚合分析。 Pipeline 的分析结果会输出到原结果中&#xff0c;根据位置的不同&#xff0c;分为两类&#xff1a; Sibling - 结果和现有…

③【Docker】Docker部署Nginx

个人简介&#xff1a;Java领域新星创作者&#xff1b;阿里云技术博主、星级博主、专家博主&#xff1b;正在Java学习的路上摸爬滚打&#xff0c;记录学习的过程~ 个人主页&#xff1a;.29.的博客 学习社区&#xff1a;进去逛一逛~ ③【Docker】Docker部署Nginx docker拉取nginx…

聚焦两会:数字化再加速,VR全景助力制造业转型

近年来&#xff0c;随着信息技术、人工智能、VR虚拟现实等新兴技术的不断涌现&#xff0c;数字化正日益成为推动当今经济发展的新驱动力。在不久前的两会上&#xff0c;数字化经济和创新技术再度成为热门话题&#xff1a; 国务院总理李强作政府工作报告&#xff1a; 要深入推…

【Session】Tomcat Session 集群

设备 nginx&#xff1a;192.168.67.11 tomcat1&#xff1a;192.168.67.12 tomcat2&#xff1a;192.168.67.13安装nginx &#xff08;192.168.67.11&#xff09; #关闭防火墙和安全机制 [roottest1 ~]# systemctl stop firewalld [roottest1 ~]# setenforce 0#安装epel源 [ro…

Postman接口做关联测试的方法步骤

应用场景 假设下一个接口登录需要上一个接口的返回值&#xff0c;例如请求需要先登录获取到token&#xff0c;下一个请求要携带对应的token才能进行请求 方法&#xff1a;通过设置全局变量/环境变量 方法一&#xff1a;设置全局变量 1.先请求登录接口&#xff0c;请求成功之后…

HarmonyOS ArkTS 开发基础/语言

目录 一、ArkUI (方舟开发框架) 概述 1.1 基本概念 1.2 两种开发范式 1.3 不同应用类型支持的开发范式 二、ArkTS 声明式开发范式 2.1 开发能力 2.2 整体架构 三、ArkTS 基础类型 3.1 Any 类型 3.2 数字类型 3.3 字符串类型 3.4 布尔类型 3.5 联合类型 3.6 数组类…

【leetcode】67.二进制求和

前言&#xff1a;剑指offer刷题系列 问题&#xff1a; 给你两个二进制字符串 a 和 b &#xff0c;以二进制字符串的形式返回它们的和。 示例&#xff1a; 输入&#xff1a;a "1010", b "1011" 输出&#xff1a;"10101"思路1&#xff1a; …

Kotlin,简直是 Java 的 Pro Max!(笔记4 协程篇)

目录 Kotlin 协程 为什么引入协程&#xff1f; 协程和线程有什么区别&#xff1f; 协程的使用 依赖 GlobalScope.launch runBlocking 创建多个协程 delay suspend 关键字 coroutineScope job async withContext 函数 Kotlin 协程 为什么引入协程&#xff1f; 1. …

GESP图形化编程二级认证真题 2024年3月

GESP 图形化二级试卷 &#xff08;满分&#xff1a;100 分 考试时间&#xff1a;120 分钟&#xff09; 一、单选题&#xff08;共 10 题&#xff0c;每题 3 分&#xff0c;共 30 分&#xff09; 1、小杨的父母最近刚刚给他买了一块华为手表&#xff0c;他说手表上跑的是鸿…

蓝桥杯2023省赛:矩阵总面积|模拟、数学(几何)

题目链接&#xff1a; 0矩形总面积 - 蓝桥云课 (lanqiao.cn) 说明&#xff1a; 参考文章&#xff1a;矩形总面积计算器&#xff1a;计算两个矩形的总面积&#xff0c;包括重叠区域_矩形r1的左下角坐标为x1, yl 、宽度为w1、高度为h1, 矩形r2的左下角坐标为x2,y2、宽-CSDN博客…

React 系列 之 React Hooks(一) JSX本质、理解Hooks

借鉴自极客时间《React Hooks 核心原理与实战》 JSX语法的本质 可以认为JSX是一种语法糖&#xff0c;允许将html和js代码进行结合。 JSX文件会通过babel编译成js文件 下面有一段JSX代码&#xff0c;实现了一个Counter组件 import React from "react";export defau…

【OJ比赛日历】快周末了,不来一场比赛吗? #03.23-03.29 #16场

CompHub[1] 实时聚合多平台的数据类(Kaggle、天池…)和OJ类(Leetcode、牛客…&#xff09;比赛。本账号会推送最新的比赛消息&#xff0c;欢迎关注&#xff01; 以下信息仅供参考&#xff0c;以比赛官网为准 目录 2024-03-23&#xff08;周六&#xff09; #7场比赛2024-03-24…

Unity InputField实现框自适应内容简便方法

要实现InputField框自适应输入内容&#xff0c;除了通过代码进行处理&#xff0c;还可以是使用以下简便的方法。 1、创建InputField组件&#xff1a;右键->UI->Input Field -TextMeshPro。 2、把Input Field Settings中的Line Type设置为Multi Line Newline模式&#x…