SpringBoot + Vue3邮件验证码功能的实现

发布于:2024-04-01 ⋅ 阅读:(87) ⋅ 点赞:(0)

后端

  • SpringBoot
  • maven
  • mysql
  • IDEA

后端负责编写邮件发送的接口逻辑,具体流程如下:

  • 引入相关依赖
  • 配置邮箱信息
  • 编写邮件发送服务接口
  • OK

引入依赖

<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-mail -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-mail</artifactId>
    <version>2.6.7</version>
</dependency>

配置邮箱信息

首先要明白一点,我们实现邮件发送需要一个用来发送邮件到目标用户邮箱里的邮箱,因此,这一步必不可少,也是最主要的一点。关于邮箱配置的具体信息在我的论文一篇文章中已经做过详细的操作说明,为了节省时间,这里就不再拿出来赘述了,建议参考文档CSDN文章 开发必会:SpringBoot邮件集成图文详解

通过你的努力阅读,相信邮箱相关的配置已经做好了,那么现在我们需要将邮箱的信息配置到项目的配置文件application.yml/properties中,参考如下:

  # 配置邮箱
  mail:
    host: smtp.163.com
    username: ilikexff@163.com
    password: 这里放你上面配置好的邮件密码或者授权码
    default-encoding: UTF-8
    protocol: smtp
    port: 465
    properties:
      mail:
        smtp:
          auth: true
          socketFactory:
            class: javax.net.ssl.SSLSocketFactory
            port: 465
          ssl:
            enable: true
          starttls:
            enable: true
            required: true

配置关注做好之后,开始着手开发邮件发送的服务接口;


编写邮件发送服务接口

基本流程:

  1. 编写邮件发送接口;
  2. 编写接口实现
  3. 编写controller层
  4. 接口基本测试
  5. 完成开发

编写服务层的接口,接口代码如下:

/**
 * ===================我亦无它================================
 * project name: big-event
 * author: 八尺妖剑
 * date: 2024/3/31
 * blog: https://www.ilikexff.cn/
 * description 邮件发送接口
 * ====================唯手熟尔===============================
 **/

package cn.ilikexff.service;

import javax.mail.MessagingException;

/**
 * 邮件发送接口
 */
public interface EmailSendService {
    /**
     * 发送普通文本邮件
     * @param to 收件人地址
     * @param subject 邮件主题
     * @param content 邮件内容
     * @param cc 抄送地址
     */
    void sendSimpleMail(String to,String subject,String content,String... cc);

    /**
     * 发送HTML邮件
     * @param to 收件人地址
     * @param subject 邮件主题
     * @param content 邮件内容
     * @param cc 抄送地址
     */
    void sendHtmlMail(String to,String subject,String content,String... cc);

    /**
     * 发送带附件的邮件
     * @param to 收件人
     * @param subject 邮件主题
     * @param content 邮件内容
     * @param filePath 附件地址
     * @param cc 抄送地址
     * @throws MessagingException 邮件发送异常
     */
    void sendAttachmentsMail(String to, String subject, String content, String filePath, String... cc) throws MessagingException;


    /**
     * 发送正文中有静态资源的邮件
     * @param to 收件人地址
     * @param subject 邮件主题
     * @param content 邮件内容
     * @param rscPath 静态资源地址
     * @param rscId 静态资源ID
     * @param cc 抄送地址
     * @throws MessagingException 发送异常
     */
    void sendResourceMail(String to, String subject, String content, String rscPath, String rscId, String... cc) throws MessagingException;


}

关于邮件发送的一些具体信息,如果你看过上面的文章,配合这里每个方法详细的注释,聪明的你应该不用我再过多解释了吧,那么继续实现该接口;

/**
 * ===================我亦无它================================
 * project name: big-event
 * author: 八尺妖剑
 * date: 2024/3/31
 * blog: https://www.ilikexff.cn/
 * description 邮件发送接口的实现
 * ====================唯手熟尔===============================
 **/

package cn.ilikexff.service.impl;

import cn.hutool.core.util.ArrayUtil;
import cn.ilikexff.service.EmailSendService;
import jakarta.mail.MessagingException;
import jakarta.mail.internet.MimeMessage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.FileSystemResource;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Service;

import java.io.File;

/**
 * 对邮件发送接口的实现
 */

@Service
public class EmailSendServiceImpl implements EmailSendService {
    @Autowired
    private JavaMailSender mailSender;
    @Value("${speing.mail.username}")
    private String from;


    /**
     * 实现普通文本邮件发送
     * @param to 收件人地址
     * @param subject 邮件主题
     * @param content 邮件内容
     * @param cc 抄送地址
     */
    @Override
    public void sendSimpleMail(String to, String subject, String content, String... cc) {
        SimpleMailMessage message = new SimpleMailMessage();
        message.setFrom(from);
        message.setTo(to);
        message.setSubject(subject);
        message.setText(content);
        if(ArrayUtil.isNotEmpty(cc)){
            message.setCc(cc);
        }
        mailSender.send(message);
    }

    /**
     * 实现发送HTML类型邮件
     * @param to 收件人地址
     * @param subject 邮件主题
     * @param content 邮件内容
     * @param cc 抄送地址
     */
    @Override
    public void sendHtmlMail(String to, String subject, String content, String... cc) throws MessagingException {

        MimeMessage message;
        message = mailSender.createMimeMessage();
        MimeMessageHelper helper;
        helper = new MimeMessageHelper(message, true);
        helper.setFrom(from);
        helper.setTo(to);
        helper.setSubject(subject);
        helper.setText(content,true);
        if(ArrayUtil.isNotEmpty(cc)){
            helper.setCc(cc);
        }
        mailSender.send(message);
    }

    /**
     * 实现发送带附件的邮件
     * @param to 收件人
     * @param subject 邮件主题
     * @param content 邮件内容
     * @param filePath 附件地址
     * @param cc 抄送地址
     * @throws MessagingException
     */
    @Override
    public void sendAttachmentsMail(String to, String subject, String content, String filePath, String... cc) throws MessagingException {
        MimeMessage message = mailSender.createMimeMessage();
        MimeMessageHelper helper = new MimeMessageHelper(message, true);
        helper.setFrom(from);
        helper.setTo(to);
        helper.setSubject(subject);
        helper.setText(content);
        if(ArrayUtil.isNotEmpty(cc)){
            helper.setCc(cc);
        }
        FileSystemResource file = new FileSystemResource(new File(filePath));
        String fileName = filePath.substring(filePath.lastIndexOf(File.separator)+1);
        helper.addAttachment(fileName, file);
        mailSender.send(message);
    }

    /**
     * 实现发送正文中有静态资源的邮件
     * @param to 收件人地址
     * @param subject 邮件主题
     * @param content 邮件内容
     * @param rscPath 静态资源地址
     * @param rscId 静态资源ID
     * @param cc 抄送地址
     * @throws MessagingException
     */
    @Override
    public void sendResourceMail(String to, String subject, String content, String rscPath, String rscId, String... cc) throws MessagingException {
        MimeMessage message = mailSender.createMimeMessage();
        MimeMessageHelper helper = new MimeMessageHelper(message, true);
        helper.setFrom(from);
        helper.setTo(to);
        helper.setSubject(subject);
        helper.setText(content, true);
        if (ArrayUtil.isNotEmpty(cc)) {
            helper.setCc(cc);
        }
        FileSystemResource res = new FileSystemResource(new File(rscPath));
        helper.addInline(rscId, res);
        mailSender.send(message);
    }
}

其中,下面的代码是将我们在配置文件中配置好的邮箱的用户名,也就是邮件的发送者注入到该类中,方便后面的方法使用。

@Value("${spring.mail.username}")
private String from;

接下来就是接口的编写了,新建一个controller,内容如下:

    /**
 * Handles the request to send an email verification code.
 * <p>
 * This method generates a random numeric verification code using a utility class,
 * stores the code in Redis with a specified expiration time, and sends the code to the
 * provided email address. The verification code is intended for use in registration or
 * other verification processes.
 * 
 * @param email The target email address to which the verification code will be sent.
 * @return A {@link Result} object containing the generated verification code and a success status.
 */
@GetMapping("/email/code")
public Result sendEmailCode(String email) {
    // 调用工具类生成指定长度的随机验证码
    String code = VerificationUtil.generateNumberCaptcha(6);
    // 将验证码存入redis并指定有效期,单位:秒
    ValueOperations<String, String> operation = stringRedisTemplate.opsForValue();
    operation.set("code",code,30, TimeUnit.SECONDS);
    // 发送验证码
    emailSendService.sendSimpleMail(email,"注册验证码","你的注册验证码为:"+code);
    return Result.success(code,0);
}

由于这里使用了redis保存验证码有效信息,因此需要引入对应的依赖:

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

下面是生成随机验证码的工具类:

/**
 * ===================我亦无它================================
 * project name: big-event
 * author: 八尺妖剑
 * date: 2024/3/31
 * blog: https://www.ilikexff.cn/
 * description 邮件验证码生成工具类
 * ====================唯手熟尔===============================
 **/

package cn.ilikexff.utils;
import java.util.Random;
public class VerificationUtil {
    /**
     * Generates a numeric captcha of a specified length.
     * This method creates a string composed of random digits, where the length of the string
     * is determined by the input parameter.
     * @param length The length of the captcha to generate. Must be a positive integer.
     * @return A string representing the generated numeric captcha.
     */
    public static String generateNumberCaptcha(int length) {
        Random random = new Random();
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < length; i++) {
            sb.append(random.nextInt(10));
        }
        return sb.toString();
    }
}

上面代码中用到的依赖注入:

private final EmailSendService emailSendService;
private final StringRedisTemplate stringRedisTemplate;

/**
 * 使用指定的邮件发送服务和Redis模板构造EmailSendController的实例。
 * 该构造函数允许对EmailSendService和StringRedisTemplate进行依赖注入,
 * 使得控制器能够发送邮件并与Redis进行交互,如存储验证码等操作。
 * @param emailSendService 用于发送邮件的服务。
 * @param stringRedisTemplate 用于字符串操作的Redis模板,此处用于存储验证码。
 */
public EmailSendController(EmailSendService emailSendService, StringRedisTemplate stringRedisTemplate) {
    this.emailSendService = emailSendService;
    this.stringRedisTemplate = stringRedisTemplate;
}

接口测试

测试工具postman

image-20240331124251806

至此,关于邮件验证码的后端部分就先告一段落,接下来使用Vue3写好前端页面,再回过头来进行前后端的联调,确保核心功能正常;


前端

  • Vue3
  • Element-Plus
  • WebStorm
  • JavaScript

编写表单

这个项目本来是没有注册验证码这个需求的,为了更好的演示效果,简单修改了以下原来的表单,修改后的页面如下:

image-20240331163500154

页面有了,接下来就是请求后端的接口,实现点击 获取验证码 按钮发送邮件验证码,为了实现点击之后进入倒计时禁用,这里采用v-if组件来实现,下面是核心代码:

image-20240331163700724


倒计时处理

其中的JavaScript部分如下:

const sms = reactive({
  disabled: false, // 按钮禁用状态
  total: 10, // 倒计时间隔:秒
  count: 0
})
// 邮件验证码,这段你可以忽略,根据自己得项目情况来,这个就是调用后端获取验证码的请求
const sendEmail = async () =>{
  console.log("函数被调用...")
  timerHandler()
  let result =  await sendEmailCodeService(registerData.value.email);
  console.log("邮箱:"+registerData.value.email)
  ElMessage.success("验证码已发送,请注意查收!");
}

// 倒计时实现
const timerHandler = () => {
  sms.count = sms.total
  sms.disabled = true

  let timer = setInterval(() => {
    if (sms.count > 1 && sms.count <= sms.total) {
      sms.count--
    } else {
      sms.disabled = false
      clearInterval(timer)
    }
  }, 1000)
}

差不多就是这样了,当我们点击【获取验证码】之后,该按钮会进入禁用状态并且倒计时提示,效果如下:

image-20240331164130631

本文含有隐藏内容,请 开通VIP 后查看

网站公告

今日签到

点亮在社区的每一天
去签到