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

⛰️个人主页: 蒾酒

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


目录

写在前面

上文衔接

接口设计与实现

1.接口分析

2.实现思路

3.代码实现

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

2.定义验证码业务接口

[3. 验证码业务接口实现](#3. 验证码业务接口实现)

4.控制层代码

4.测试

写在最后


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

点击跳转到学习网站

写在前面

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

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

上文衔接

实现发送验证码邮件接口的前提是项目中已经整合好了邮件服务。上篇文章介绍了springboot整合QQ邮箱SMTP服务:spring boot3整合邮件服务实现邮件发送功能_spring-boot-starter-mail springboot3-CSDN博客https://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模板枚举类

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

java 复制代码
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.定义验证码业务接口

java 复制代码
public interface CaptchaService {

    /**
     * 获取邮箱验证码
     * @param email 邮箱
     */
    void getMailCaptcha(String email);

}

3. 验证码业务接口实现

java 复制代码
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;
    }

    
    @Override
    public 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); // 重新设置过期时间为24H
            throw 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.控制层代码

java 复制代码
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博客https://blog.csdn.net/qq_62262918/article/details/135761392

发送请求

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

也收到了验证码邮件

写在最后

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

相关推荐
咩咩啃树皮10 小时前
第40篇:Vue3组件化开发精讲——组件拆分、复用、父子通信、工程化架构
java·前端·架构
鱟鲥鳚10 小时前
Spring Boot 集成 LangChain4j:从模型调用到 Tool Calling(Demo版)
java·spring boot
大模型码小白12 小时前
【Python零基础教程】继承、多态与魔法函数:面向对象编程三大核心特性详解
java·大数据·开发语言·人工智能·python·ai编程
腾渊信息科技公司12 小时前
Spring Boot对接MES实战:视觉检测数据自动同步方案
java·人工智能·spring boot·后端·计算机视觉·ai·软件需求
爱笑的源码基地13 小时前
高并发 Redis 缓存门诊HIS系统源码,含财务统计药房进销存
java·程序·门诊系统·诊所系统·云诊所源码
wuqingshun31415914 小时前
TCP超时重传机制是为了解决什么问题?
java
莫逸风16 小时前
【AgentScope 2.0】 0. 学习指南
java·llm·agent·agentscope
隔窗听雨眠16 小时前
Spring Boot在云原生时代的编程范式革新研究
spring boot·后端·云原生
z1234567898617 小时前
2026最新两款AI编程工具深度对比实测
java·数据库·ai编程
yaoxin52112317 小时前
470. Java 反射 - Member 接口与 AccessFlag
java·开发语言·python