Loading... 使用Spring Boot和Redis实现一个简易的邮箱验证 ## 引入依赖 需要使用的依赖 - thymeleaf:用于邮箱验证码模板 - mail:Spring 邮件服务 - redis:用于存储验证码 (可选) ```xml <!-- https://mvnrepository.com/artifact/org.thymeleaf/thymeleaf --> <dependency> <groupId>org.thymeleaf</groupId> <artifactId>thymeleaf</artifactId> <version>3.1.2.RELEASE</version> </dependency> <!-- https://mvnrepository.com/artifact/org.thymeleaf/thymeleaf-spring5 --> <dependency> <groupId>org.thymeleaf</groupId> <artifactId>thymeleaf-spring5</artifactId> <version>3.1.2.RELEASE</version> </dependency> <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-mail --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-mail</artifactId> <version>3.1.4</version> </dependency> <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-data-redis --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> <version>3.1.4</version> </dependency> ``` ## 邮箱账号 获取SMTP邮箱账号用于发送邮件,这里以腾讯邮箱为例 进入邮箱后点击 `设置 - 账号`,找到 `POP3/IMAP/SMTP/Exchange/CardDAV/CalDAV服务`, 开启服务,点击管理服务并生成授权码,根据提示获取授权码,请牢记授权码,因为他只会显示这一次 ![](http://storage.live.com/items/FECD80CE4F909A96!46733:/Pasted_image_20231025162417.png?authkey=!AGD_bteyytBpQDg) ![](http://storage.live.com/items/FECD80CE4F909A96!46734:/Pasted_image_20231025162517.png?authkey=!AGD_bteyytBpQDg) ![](http://storage.live.com/items/FECD80CE4F909A96!46735:/Pasted_image_20231025162648.png?authkey=!AGD_bteyytBpQDg) 接下来我们要使用到刚才获取的授权码 ## Spring 配置 在这里配置: - 邮箱信息 - thymeleaf - redis ```yaml # application.yaml spring: data: # Redis 配置 redis: host: your.redis.url # redis 地址 port: port # redis 端口 password: password # redis 密码 database: 1 lettuce: # 连接池 (可选) pool: max-idle: 16 max-active: 32 min-idle: 8 mail: host: smtp.qq.com # 填写你使用的邮箱服务的smtp地址 username: test@qq.com # 邮箱账户 password: password # 刚刚获取的授权码 port: 587 # smtp默认端口 from: test@qq.com # 邮箱账户 thymeleaf: prefix: classpath:/templates/ # 模板路径 suffix: .html # 匹配文件 mode: HTML5 encoding: UTF-8 cache: false # 关闭缓存 ``` ## 验证码模板 在`/resource/template`下创建验证码页面,code为需要传入的验证码 考虑到各家服务商的兼容性问题,所有样式改为使用行内样式 ```html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>验证码</title> </head> <body> <div class="container" style="width: 500px;height: 500px;background-color: #edf1f2;box-shadow: 1px 1px 10px rgba(136, 136, 136, 0.6);border: 1px solid rgba(136, 136, 136, 0.6);border-radius: 10px;"> <div class="text" style="padding: 50px 0 50px 0 ;text-align: center;color: #21467a;"> <span class="title" style=" font-size: 30px;font-family: '微软雅黑', serif;font-weight: bold;">登录代码</span> </div> <div class="code" style="width:300px;margin-left:100px;height: 80px;background-color: #ffffff;text-align: center"> <span style="display: inline-block;line-height: 80px;font-size: 30px;letter-spacing: 20px" th:text="${code}"></span> </div> <div style="text-align: center;margin-top: 40px;color: #426092">您正在登录Insight Hub,该验证码10分钟内有效</div> <div style="text-align: center;color: #838383;padding: 20px"> 注意:此操作可能会修改您的密码、登录邮箱或绑定手机。如非本人操作,请及时登录并修改密码以保证帐户安全 <br>(工作人员不会向你索取此验证码,请勿泄漏!) </div> <div style="color: #21467a;text-align: center;margin-top: 10px"> 2023 Insight 社区 </div> </div> </body> </html> ``` ![](http://storage.live.com/items/FECD80CE4F909A96!46732:/Pasted_image_20231025180243.png?authkey=!AGD_bteyytBpQDg) ## 代码部分 ### 邮箱工具类 需要实现的功能:发送邮件、验证邮件验证码 在发送邮件时,将验证码存入Redis并设置过期时间,需要验证的时候,就从Redis中取出来进行比对 接口 ```java /** * 邮件工具类 */ public interface EmailUtil { /** * 发送验证码至邮箱 * * @param to 收件人 * @param code 验证码 */ void sendVerifyCodeMail(String to, String code); /** * 生成六位随机验证码 * * @return 验证码 */ Integer generateCode(); } ``` 实现类 ```java import com.insight.hub.utils.EmailUtil; import jakarta.mail.MessagingException; import jakarta.mail.internet.MimeMessage; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.mail.SimpleMailMessage; import org.springframework.mail.javamail.JavaMailSender; import org.springframework.mail.javamail.MimeMessageHelper; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Component; import org.thymeleaf.TemplateEngine; import org.thymeleaf.context.Context; @Component @Slf4j public class EmailUtilImpl implements EmailUtil { private final JavaMailSender mailSender; private final TemplateEngine templateEngine; @Value("${spring.mail.from}") private String from; @Autowired public EmailUtilImpl(JavaMailSender mailSender, TemplateEngine templateEngine) { this.mailSender = mailSender; this.templateEngine = templateEngine; } // 自定义的线程池,参见下文,若不需要异步则不需要该注解 @Override @Async("AsyncTaskExecutor") public void sendVerifyCodeMail(String to, String code) { // thymeleaf模板上下文 Context context = new Context(); log.info("发送邮件"); // 设置上下文 context.setVariable("code", code); // 执行模板引擎,将验证码传递到页面 String templateContent = templateEngine.process("verify-code", context); // 创建邮件消息 MimeMessage message = mailSender.createMimeMessage(); try { MimeMessageHelper helper = new MimeMessageHelper(message, true); // 设置发件人 helper.setFrom(from); // 设置收件人 helper.setTo(to); // 邮件主题 helper.setSubject("登录验证码"); // 邮件文本 helper.setText(templateContent, true); // 发送邮件 mailSender.send(message); log.info("邮件发送完成"); } catch (MessagingException e) { throw new RuntimeException(e); } } @Override public Integer generateCode() { // 简易的随机6位数字验证码 Random random = new Random(); return 100000 + random.nextInt(900000); } } ``` 至此,调用`sendVerifyCodeMail()`就可以发送邮件了 ### 自定义线程池 在调用`mailSender.send(message);`会造成线程阻塞,导致前端需要等待邮件发送完成才能获得响应,因此需要为邮件发送配置异步线程 创建线程池 ```java @Configuration public class ThreadPoolConfig { @Bean("AsyncTaskExecutor") public AsyncTaskExecutor taskExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); // 设置核心线程数 executor.setCorePoolSize(8); // 设置最大线程数 executor.setMaxPoolSize(16); // 设置队列容量 executor.setQueueCapacity(32); // 设置线程活跃时间(秒) executor.setKeepAliveSeconds(60); // 设置默认线程名称 executor.setThreadNamePrefix("task-"); // 设置拒绝策略 executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); executor.initialize(); return executor; } } ``` 然后在我们写的方法上添加`@Async("AsyncTaskExecutor")`就可以了 ### 测试相关 由于使用了异步,导致我们的测试类无法获取异步方法的结果,所以我们需要自定义一个异步执行器来进行测试 非常简单,添加一个AsyncConfigurer的实现类 ```java /** * 测试用AsyncExecutor */ @Component public class AsyncConfig implements AsyncConfigurer { @Override public Executor getAsyncExecutor() { return new SyncTaskExecutor(); } } ``` 然后在测试类中引入即可 ```java @SpringBootTest(classes = InsightHubApplication.class,webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @RunWith(SpringJUnit4ClassRunner.class) @Import(AsyncConfig.class) public class EmailTest { @Resource private EmailUtil emailUtil; @Test public void testSendSimpleMail(){ emailUtil.sendVerifyCodeMail("test@mail.com", String.valueOf(emailUtil.generateCode())); } } ``` ## 总结 核心部分就是 - 使用Thymeleaf模板语言编写 验证码页面 - 使用模板引擎传递验证码 - 使用JavaMailSender发送HTML邮件 - 配置异步方法 最后修改:2023 年 10 月 25 日 © 允许规范转载 打赏 赞赏作者 支付宝微信 赞 如果觉得我的文章对你有用,请随意赞赏