spring boot3登录开发-2(3邮件验证码接口实现)

2024-06-06 08:28

本文主要是介绍spring boot3登录开发-2(3邮件验证码接口实现),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

 

⛰️个人主页:     蒾酒

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


目录

写在前面

上文衔接

接口设计与实现 

1.接口分析

2.实现思路

3.代码实现

1.定义验证码短信HTML模板枚举类

2.定义验证码业务接口

3. 验证码业务接口实现

4.控制层代码

4.测试

写在最后


最近发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。

    点击跳转到学习网站

写在前面

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

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

上文衔接

实现发送验证码邮件接口的前提是项目中已经整合好了邮件服务。上篇文章介绍了springboot整合QQ邮箱SMTP服务:spring boot3整合邮件服务实现邮件发送功能_spring-boot-starter-mail springboot3-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/qq_62262918/article/details/139244604

接口设计与实现 

1.接口分析

关键点:

  1. 邮件验证码60s发送间隔
  2. 连发多次封禁24H
  3. 5分钟有效期

2.实现思路

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

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

注意事项:

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

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

整合redis:

Spring Boot3整合Redis_springboot3整合redis-CSDN博客文章浏览阅读6.6k次,点赞103次,收藏111次。⛰️个人主页: 蒾酒🔥系列专栏:《spring boot实战》目录前置条件1.导依赖2.配置连接信息以及连接池参数3.配置序列化方式4.编写测试已经初始化好一个spring boot项目且版本为3X,项目可正常启动。作者版本为3.2.2初始化教程:新版idea(2023)创建spring boot3项目-CSDN博客https://blog.csdn.net/qq_62262918/article/details/135785412?spm=1001.2014.3001.5501pom.xml:_springboot3整合redishttps://blog.csdn.net/qq_62262918/article/details/136067550?spm=1001.2014.3001.5501

3.代码实现

1.定义验证码短信HTML模板枚举类

在实际业务中可能需要发送各种类型的邮件通知,将不同类型的邮件定义为模板维护在枚举中也是种不错选择。

import lombok.Getter;
import java.util.Locale;/*** @author mijiupro*/
@Getter
public enum EmailTemplateEnum {// 验证码邮件VERIFICATION_CODE_EMAIL_HTML("<html><body>用户你好,你的验证码是:<h1>%s</h1></body></html>","登录验证"),// 用户被封禁邮件通知USER_BANNED_EMAIL("用户你好,你已经被管理员封禁,封禁原因:%s", "封禁通知"),;private final String template;private final String subject;EmailTemplateEnum(String template, String subject) {this.template = template;this.subject = subject;}public String value() {return this.template;}/*** 模板参数填充* @param args 参数集* @return 填充后的字符串*/public String set(Object... args) {return String.format(Locale.ROOT, this.template, args);}
}

2.定义验证码业务接口

public interface CaptchaService {/*** 获取邮箱验证码* @param email 邮箱*/void getMailCaptcha(String email);}

3. 验证码业务接口实现

import com.mijiu.commom.enumerate.EmailTemplateEnum;
import com.mijiu.commom.exception.GeneralBusinessException;
import com.mijiu.api.email.EmailApi;
import com.mijiu.api.sms.SmsApi;
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 SmsApi smsUtil;private final EmailApi emailUtil;public CaptchaServiceImpl(StringRedisTemplate stringRedisTemplate, SmsApi smsUtil, EmailApi emailUtil) {this.stringRedisTemplate = stringRedisTemplate;this.smsUtil = smsUtil;this.emailUtil = emailUtil;}@Overridepublic void getMailCaptcha(String email) {getCaptcha("login:email:captcha:" + email);}private void getCaptcha(String hashKey) {BoundHashOperations<String, String, String> hashOps = stringRedisTemplate.boundHashOps(hashKey);// 初始检查String lastSendTimestamp = hashOps.get("lastSendTimestamp");String sendCount = hashOps.get("sendCount");String captcha = hashOps.get("captcha");// 判断发送次数是否超过限制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);}// 发送邮件或短信sendCaptcha(hashKey, captcha);// 更新 Redis 中的信息hashOps.put("captcha", captcha);hashOps.put("lastSendTimestamp", String.valueOf(System.currentTimeMillis()));hashOps.put("sendCount", String.valueOf(newSendCount));hashOps.expire(5, TimeUnit.MINUTES); // 设置过期时间为5分钟}private void sendCaptcha(String hashKey, String captcha) {// 根据hashKey判断是发送邮件还是短信,然后调用相应的发送方法if("email".equals(hashKey.split(":")[1])){if (!emailUtil.sendHtmlEmail(EmailTemplateEnum.VERIFICATION_CODE_EMAIL_HTML.getSubject(),EmailTemplateEnum.VERIFICATION_CODE_EMAIL_HTML.set(captcha),hashKey.split(":")[3])) {throw new GeneralBusinessException("发送邮件失败");}} else if ( "sms".equals(hashKey.split(":")[1])) {if (!smsUtil.sendSms(hashKey.split(":")[3], captcha)) {throw new GeneralBusinessException("发送短信失败");}}}}

4.控制层代码

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("/mail-captcha/{email}")@Operation(summary = "获取邮箱验证码")public void getEmailCaptcha(@PathVariable String email) {captchaService.getMailCaptcha(email);}}

到这里接口已经实现。

4.测试

用swagger3进行测试:

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

发送请求

 这是redis已经缓存了验证码信息

 

也收到了验证码邮件

 

写在最后

 邮件验证码接口功能的设计与实现到这里就结束了,任何问题评论区或私信讨论,欢迎指正。

这篇关于spring boot3登录开发-2(3邮件验证码接口实现)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1035583

相关文章

Java中流式并行操作parallelStream的原理和使用方法

《Java中流式并行操作parallelStream的原理和使用方法》本文详细介绍了Java中的并行流(parallelStream)的原理、正确使用方法以及在实际业务中的应用案例,并指出在使用并行流... 目录Java中流式并行操作parallelStream0. 问题的产生1. 什么是parallelS

C++中unordered_set哈希集合的实现

《C++中unordered_set哈希集合的实现》std::unordered_set是C++标准库中的无序关联容器,基于哈希表实现,具有元素唯一性和无序性特点,本文就来详细的介绍一下unorder... 目录一、概述二、头文件与命名空间三、常用方法与示例1. 构造与析构2. 迭代器与遍历3. 容量相关4

Java中Redisson 的原理深度解析

《Java中Redisson的原理深度解析》Redisson是一个高性能的Redis客户端,它通过将Redis数据结构映射为Java对象和分布式对象,实现了在Java应用中方便地使用Redis,本文... 目录前言一、核心设计理念二、核心架构与通信层1. 基于 Netty 的异步非阻塞通信2. 编解码器三、

C++中悬垂引用(Dangling Reference) 的实现

《C++中悬垂引用(DanglingReference)的实现》C++中的悬垂引用指引用绑定的对象被销毁后引用仍存在的情况,会导致访问无效内存,下面就来详细的介绍一下产生的原因以及如何避免,感兴趣... 目录悬垂引用的产生原因1. 引用绑定到局部变量,变量超出作用域后销毁2. 引用绑定到动态分配的对象,对象

SpringBoot基于注解实现数据库字段回填的完整方案

《SpringBoot基于注解实现数据库字段回填的完整方案》这篇文章主要为大家详细介绍了SpringBoot如何基于注解实现数据库字段回填的相关方法,文中的示例代码讲解详细,感兴趣的小伙伴可以了解... 目录数据库表pom.XMLRelationFieldRelationFieldMapping基础的一些代

一篇文章彻底搞懂macOS如何决定java环境

《一篇文章彻底搞懂macOS如何决定java环境》MacOS作为一个功能强大的操作系统,为开发者提供了丰富的开发工具和框架,下面:本文主要介绍macOS如何决定java环境的相关资料,文中通过代码... 目录方法一:使用 which命令方法二:使用 Java_home工具(Apple 官方推荐)那问题来了,

Java HashMap的底层实现原理深度解析

《JavaHashMap的底层实现原理深度解析》HashMap基于数组+链表+红黑树结构,通过哈希算法和扩容机制优化性能,负载因子与树化阈值平衡效率,是Java开发必备的高效数据结构,本文给大家介绍... 目录一、概述:HashMap的宏观结构二、核心数据结构解析1. 数组(桶数组)2. 链表节点(Node

Java AOP面向切面编程的概念和实现方式

《JavaAOP面向切面编程的概念和实现方式》AOP是面向切面编程,通过动态代理将横切关注点(如日志、事务)与核心业务逻辑分离,提升代码复用性和可维护性,本文给大家介绍JavaAOP面向切面编程的概... 目录一、AOP 是什么?二、AOP 的核心概念与实现方式核心概念实现方式三、Spring AOP 的关

详解SpringBoot+Ehcache使用示例

《详解SpringBoot+Ehcache使用示例》本文介绍了SpringBoot中配置Ehcache、自定义get/set方式,并实际使用缓存的过程,文中通过示例代码介绍的非常详细,对大家的学习或者... 目录摘要概念内存与磁盘持久化存储:配置灵活性:编码示例引入依赖:配置ehcache.XML文件:配置

Java 虚拟线程的创建与使用深度解析

《Java虚拟线程的创建与使用深度解析》虚拟线程是Java19中以预览特性形式引入,Java21起正式发布的轻量级线程,本文给大家介绍Java虚拟线程的创建与使用,感兴趣的朋友一起看看吧... 目录一、虚拟线程简介1.1 什么是虚拟线程?1.2 为什么需要虚拟线程?二、虚拟线程与平台线程对比代码对比示例:三