⛰️个人主页: 蒾酒
🔥系列专栏:《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博客https://blog.csdn.net/qq_62262918/article/details/139244604
接口设计与实现
1.接口分析
关键点:
- 邮件验证码60s发送间隔
- 连发多次封禁24H
- 5分钟有效期
2.实现思路
接口接收一个邮箱号参数,先去redis查询该邮箱是否有未过期的验证码,如果有先判断发送次数,超过5次,先修改过期时间为24H,再抛发送次数过多异常,少于五次,就再判断发送频率,距离上次发送间隔少于60s,就抛出发送频繁异常, 如果没有未过期的验证码,代表这是第一次发送就直接生成验证码发送短信,接着redis存入一个哈希,key通过手机号生成,value就是,三个键值对,验证码,上次发送时间戳,发送次数(初始为1),默认过期时间5分钟。
- 接收手机号参数。
- 查询 Redis 中该手机号是否有未过期的验证码。
- 如果有未过期的验证码:
- 判断发送次数是否超过5次,如果超过5次,修改过期时间为24小时并抛出发送次数过多异常。
- 如果发送次数未超过5次,再判断距离上次发送时间间隔是否少于60秒,如果少于60秒,抛出发送频繁异常。
- 如果没有未过期的验证码,代表第一次发送:
- 生成验证码并发送短信。
- 将验证码信息存入 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("用户你好,你的验证码是:%s
","登录验证"), // 用户被封禁邮件通知 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; } @Override public void getMailCaptcha(String email) { getCaptcha("login:email:captcha:" + email); } private void getCaptcha(String hashKey) { BoundHashOperationshashOps = 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.控制层代码
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博客
发送请求
也收到了验证码邮件
写在最后
邮件验证码接口功能的设计与实现到这里就结束了,任何问题评论区或私信讨论,欢迎指正。
还没有评论,来说两句吧...