SpringBoot 轻松实现发送邮箱验证码+Redis缓存(附带HTML模板)

2024-06-04 7321阅读

文章目录

    • 前言
    • 准备工作
    • 代码实现
      • 引入依赖
      • 配置文件
      • 编写相关工具类
      • 实现验证码发送代码
      • 测试

        前言

        在日常生活中,通过发送邮箱验证码来实现用户注册、密码重置和账户验证等功能在许多现代应用程序中非常常见,这里采用hutool工具包封装的一些类实现发送邮箱验证码的功能。

        准备工作

        开启POP3/SMTP服务并获取授权码,具体操作很简单,这里就不演示了

        网址:账号与安全 (qq.com)

        SpringBoot 轻松实现发送邮箱验证码+Redis缓存(附带HTML模板) 第1张

        代码实现

        引入依赖

                
                
                    cn.hutool
                    hutool-all
                    5.8.11
                
                
                
                    org.springframework.boot
                    spring-boot-starter-thymeleaf
                    3.2.0
                
                
                
                    com.sun.mail
                    javax.mail
                    1.6.2
                
                
                
                    org.springframework.boot
                    spring-boot-starter-data-redis
                
        

        配置文件

        为了便于修改配置信息,因此要将邮件的相关配置写到配置文件application.yaml中

        1. 编写配置类
         @Component
         @ConfigurationProperties(prefix = "captcha.email")
         @Data
         public class EmailProperties {
             /**
              * 邮箱地址(注意:如果使用foxmail邮箱,此处user为qq号)
              */
             private String user;
             /**
              * 发件人昵称(必须正确,否则发送失败)
              */
             private String from;
             /**
              * 邮件服务器的SMTP地址
              */
             private String host;
         
             /**
              * 邮件服务器的SMTP端口
              */
             private Integer port;
         
             /**
              * 密码(授权码)
              */
             private String password;
         
             /**
              * 验证码过期时间
              */
             private Integer expireTime;
         }
        
        1. 编写相关配置信息到application.yaml文件
         captcha:
           email:
             from: 詩筠
             password: dkhfsiudhfsd # 这里我随便填的
             host: smtp.qq.com
             port: 465
             user: XXXXX@qq.com
             expire-time: 300 # 300秒
        

        编写相关工具类

        验证码工具类VerifyCodeUtil:

        /**
         * 验证码工具类
         * @author shijun
         * @date 2024/05/10
         */
        public class VerifyCodeUtil {
            //邮箱正则表达式
            private static final Pattern EMAIL_PATTERN = Pattern.compile("^\\w+((-\\w+)|(\\.\\w+))*\\@[A-Za-z0-9]+((\\.|-)[A-Za-z0-9]+)*\\.[A-Za-z0-9]+$");
            //验证码的字符集
            private static final String CODES = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
            //验证码的长度,可以根据需求进行修改
            private static final int CODE_LENGTH = 6;
            /**
             * 生成验证码
             *
             * @return {@link String }
             */
            public static String generateVerifyCode() {
                Random random = new Random();
                StringBuilder verifyCode = new StringBuilder(CODE_LENGTH);
                for (int i = 0; i  
        

        Redis工具类RedisUtils:

        @Component
        @AllArgsConstructor
        public class RedisUtils {
            private RedisTemplate redisTemplate;
            /**
             * 普通获取键对应值
             *
             * @param key 键
             * @return 值
             */
            public Object get(String key) {
                return key == null ? null : redisTemplate.opsForValue().get(key);
            }
            /**
             * 普通设置键值
             *
             * @param key   键
             * @param value 值
             * @return true成功 false失败
             */
            public boolean set(String key, Object value) {
                try {
                    redisTemplate.opsForValue().set(key, value);
                    return true;
                } catch (Exception e) {
                    return false;
                }
            }
            /**
             * 普通设置键值并设置过期时间
             *
             * @param key   键
             * @param value 值
             * @param time  时间(秒) time要大于0 如果time小于等于0 将设置无限期
             * @return true成功 false 失败
             */
            public boolean set(String key, Object value, long time) {
                try {
                    if (time > 0) {
                        redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
                    } else {
                        set(key, value);
                    }
                    return true;
                } catch (Exception e) {
                    return false;
                }
            }
            /**
             * 删除缓存
             *
             * @param key 键
             * @return 是否成功
             */
            public boolean del(String key) {
                return redisTemplate.delete(key);
            }
            /**
             * 指定缓存的失效时间
             *
             * @param key  键值 
             * @param time 时间(秒) 
             */
            public boolean expire(String key, long time) {
                try {
                    if (time > 0) {
                        redisTemplate.expire(key, time, TimeUnit.SECONDS);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                    return false;
                }
                return true;
            }
        }
        

        实现验证码发送代码

        1. 设计邮箱验证码模板

          为了改善用户体验,因此要设计一个好看的验证码界面

        编写EmailVerificationCode.html文件,放到src/main/resources/templates目录下:

        SpringBoot 轻松实现发送邮箱验证码+Redis缓存(附带HTML模板) 第2张

        
        
            
            邮箱验证码
            
                table {
                    width: 700px;
                    margin: 0 auto;
                }
                #top {
                    width: 700px;
                    border-bottom: 1px solid #ccc;
                    margin: 0 auto 30px;
                }
                #top table {
                    font: 12px Tahoma, Arial, 宋体;
                    height: 40px;
                }
                #content {
                    width: 680px;
                    padding: 0 10px;
                    margin: 0 auto;
                }
                #content_top {
                    line-height: 1.5;
                    font-size: 14px;
                    margin-bottom: 25px;
                    color: #4d4d4d;
                }
                #content_top strong {
                    display: block;
                    margin-bottom: 15px;
                }
                #content_top strong span {
                    color: #f60;
                    font-size: 16px;
                }
                #verificationCode {
                    color: #f60;
                    font-size: 24px;
                }
                #content_bottom {
                    margin-bottom: 30px;
                }
                #content_bottom small {
                    display: block;
                    margin-bottom: 20px;
                    font-size: 12px;
                    color: #747474;
                }
                #bottom {
                    width: 700px;
                    margin: 0 auto;
                }
                #bottom div {
                    padding: 10px 10px 0;
                    border-top: 1px solid #ccc;
                    color: #747474;
                    margin-bottom: 20px;
                    line-height: 1.3em;
                    font-size: 12px;
                }
                #content_top strong span {
                    font-size: 18px;
                    color: #FE4F70;
                }
                #sign {
                    text-align: right;
                    font-size: 18px;
                    color: #000000;
                    font-weight: bold;
                }
                #verificationCode {
                    height: 100px;
                    width: 680px;
                    text-align: center;
                    margin: 30px 0;
                }
                #verificationCode div {
                    height: 100px;
                    width: 680px;
                }
                .button {
                    color: #FE4F70;
                    margin-left: 10px;
                    height: 80px;
                    width: 80px;
                    resize: none;
                    font-size: 42px;
                    border: none;
                    outline: none;
                    padding: 10px 15px;
                    background: #ededed;
                    text-align: center;
                    border-radius: 17px;
                    box-shadow: 6px 6px 12px #cccccc,
                    -6px -6px 12px #ffffff;
                }
                .button:hover {
                    box-shadow: inset 6px 6px 4px #d1d1d1,
                    inset -6px -6px 4px #ffffff;
                }
            
        
        
        
        尊敬的用户,您好! 您正在进行注册账号操作,请在验证码中输入以下验证码完成操作:
        [[${a}]]
        注意:此操作可能会修改您的密码、登录邮箱或绑定手机。如非本人操作,请及时登录并修改密码以保证帐户安全
        (工作人员不会向你索取此验证码,请勿泄漏!)

        此为系统邮件,请勿回复
        请保管好您的邮箱,避免账号被他人盗用

        ——詩筠

        1. 编写Controller层代码
         /**
          * 验证码接口
          *
          * @author shijun
          * @date 2024/05/10
          */
         @RestController
         @Tag(name = "验证码接口")
         @RequestMapping("/captcha")
         @RequiredArgsConstructor
         public class CaptchaController {
         
             private final CaptchaService captchaService;
             private final TemplateEngine templateEngine;
         
             @Operation(summary = "发送邮箱验证码")
             @Parameter(name = "email",description = "要发送的邮箱")
             @GetMapping("/email-captcha")
             public Result sendEmailCaptcha(String email) {
                 captchaService.sendEmailCaptcha(email);
                 return Result.success("发送成功");
             }
         
         }
        
        1. 编写Service层代码
         public interface CaptchaService {
             /**
              * 发送验证码
              * @param email 邮箱
              */
             void sendEmailCaptcha(String email);
         }
         
        
        1. 编写Service实现类

          这里涉及的BusinessException类是一个自定义异常类,用来进行业务异常处理。

          可以参考这篇文章进行学习:Spring Boot3自定义异常及全局异常捕获_springboot是如何自定义异常并捕获的-CSDN博客

         @Service
         @RequiredArgsConstructor
         public class CaptchaServiceImpl implements CaptchaService {
         
             public static final String CAPTCHA_CODE = "captcha-email-";
             private final EmailProperties emailProperties;
             private final RedisUtils redisUtils;
             private final TemplateEngine templateEngine;
         
             /**
              * 发送邮件验证码
              * @param email 邮箱
              */
             @Override
             public void sendEmailCaptcha(String email) {
                 // 验证邮件配置是否完整
                 validateEmailProperties();
         
                 // 验证邮箱格式
                 if (!VerifyCodeUtil.checkEmail(email)) {
                     throw new BusinessException(ResultEnum.EMAIL_FORMAT_ERROR);
                 }
         
                 // 生成或获取验证码
                 String captcha = getCaptcha(email);
         
                 // 生成邮件内容
                 String content = generateEmailContent(captcha);
         
                 // 发送邮件
                 List list = Collections.singletonList(email);
                 sendEmail(list, content);
             }
         
             /**
              * 判断邮件配置是否完整
              */
             private void validateEmailProperties() {
                 if (emailProperties.getUser() == null || emailProperties.getPassword() == null || emailProperties.getFrom() == null || emailProperties.getHost() == null || emailProperties.getPort() == null) {
                     throw new BusinessException(ResultEnum.EMAIL_VERIFICATION_CODE_CONFIGURATION_EXCEPTION);
                 }
             }
         
             /**
              * 获取验证码
              *
              * @param email 邮箱地址,用于生成和存储验证码。
              * @return {@link String} 返回生成的验证码。
              */
             private String getCaptcha(String email) {
                 // 根据邮箱生成Redis键名
                 String redisKey = CAPTCHA_CODE + email;
                 // 尝试从Redis获取现有的验证码
                 Object oldCode = redisUtils.get(redisKey);
                 if (oldCode == null) {
                     // 如果验证码不存在,生成新的验证码
                     String captcha = VerifyCodeUtil.generateVerifyCode();
                     // 将新生成的验证码存储到Redis,并设置过期时间
                     boolean saveResult = redisUtils.set(redisKey, captcha, emailProperties.getExpireTime());
                     if (!saveResult) {
                         // 如果存储失败,抛出异常
                         throw new RedisException(MessageConstant.REDIS_SERVICE_EXCEPTION_VERIFICATION_CODE_SAVE_FAILED);
                     }
                     return captcha;
                 } else {
                     // 如果验证码存在,重置其在Redis中的过期时间
                     boolean expireResult = redisUtils.expire(redisKey, emailProperties.getExpireTime());
                     if (!expireResult) {
                         throw new RedisException(MessageConstant.RESET_VERIFICATION_CODE_FAILED);
                     }
                     return String.valueOf(oldCode);
                 }
             }
         
         
             /**
              * 生成邮件内容
              * @param captcha 验证码
              * @return {@link String } 邮件内容
              */
             private String generateEmailContent(String captcha) {
                 Context context = new Context();
                 context.setVariable("verifyCode", Arrays.asList(captcha.split("")));
                 return templateEngine.process("EmailVerificationCode.html", context);
             }
         
             /**
              * 发送邮件
              * @param list
              * @param content 邮件内容
              */
             private void sendEmail(List list, String content) {
                 MailAccount account = createMailAccount();
                 try {
                     Mail.create(account)
                             .setTos(list.toArray(new String[0]))
                             .setTitle(MessageConstant.EMAIL_VERIFICATION_CODE)
                             .setContent(content)
                             .setHtml(true)
                             .setUseGlobalSession(false)
                             .send();
                 } catch (Exception e) { // 捕获更广泛的异常
                     throw new BusinessException(ResultEnum.EMAIL_SENDING_EXCEPTION);
                 }
             }
         
             /**
              * 创建邮件账户
              * @return {@link MailAccount } 邮件账户
              */
             private MailAccount createMailAccount() {
                 MailAccount account = new MailAccount();
                 account.setAuth(true);
                 account.setHost(emailProperties.getHost());
                 account.setPort(emailProperties.getPort());
                 account.setFrom(emailProperties.getFrom());
                 account.setUser(emailProperties.getUser());
                 account.setPass(emailProperties.getPassword());
                 account.setSslEnable(true);
                 account.setStarttlsEnable(true);
                 return account;
             }
         
         }
         
        

        测试

        SpringBoot 轻松实现发送邮箱验证码+Redis缓存(附带HTML模板) 第3张

        SpringBoot 轻松实现发送邮箱验证码+Redis缓存(附带HTML模板) 第4张


        参考文章

        邮件工具-MailUtil | Hutool

        手把手教你通过SpringBoot实现邮箱注册码验证_springboot+vue简单实现邮箱注册-CSDN博客

        springboot实现邮箱验证码功能_springboot邮箱验证码-CSDN博客

        SpringBoot 发送邮箱验证码(HTML模板)-阿里云开发者社区 (aliyun.com)


    免责声明:我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理! 图片声明:本站部分配图来自人工智能系统AI生成,觅知网授权图片,PxHere摄影无版权图库和百度,360,搜狗等多加搜索引擎自动关键词搜索配图,如有侵权的图片,请第一时间联系我们,邮箱:ciyunidc@ciyunshuju.com。本站只作为美观性配图使用,无任何非法侵犯第三方意图,一切解释权归图片著作权方,本站不承担任何责任。如有恶意碰瓷者,必当奉陪到底严惩不贷!

    目录[+]