Spring Boot 整合网易163邮箱发送邮件实现找回密码功能

在开发用户系统时,发送邮件是一项常见需求,例如用户忘记密码时,通过邮箱发送验证码来验证身份并重置密码。本文将结合 Spring Boot 和 163 邮箱,演示如何实现邮件发送功能。


一、前提条件

普通用户的 163 邮箱可以在 Spring Boot 项目中用于发送邮件,但需要完成以下配置:

1. 登录 163 邮箱

使用普通账号登录 163 邮箱官网

2. 开启 SMTP 服务

  1. 点击邮箱右上角「设置」→「POP3/SMTP/IMAP」;
  2. 在 SMTP 服务选项中勾选「开启 SMTP 服务」;
  3. 按提示完成手机验证(163 邮箱要求绑定手机号才能开启该服务)。

3. 获取授权码

  1. 开启 SMTP 后,页面会提示生成授权码;
  2. 按照提示生成 16 位授权码(例如 abcdefghijklmnop),保存好;
  3. 该授权码将在 Spring Boot 邮件配置中替代邮箱密码。

⚠️ 注意:普通 163 邮箱每天发送量有限,适合开发和测试用途。


二、Spring Boot 邮件依赖

pom.xml 中添加 Spring Boot 邮件依赖:

xml 复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-mail</artifactId>
</dependency>

三、Spring Boot 邮件配置

application.ymlapplication.properties 中添加 163 邮箱 SMTP 配置:

yaml 复制代码
spring:
# 配置邮箱服务器,账号密码等
  mail:
    host: smtp.163.com
    username: 1888888888@163.com
    password: agagtfgsdhd
    code:
      overtime: 5

其中 password 是生成的授权码,而非邮箱密码。


四、邮件发送服务实现

创建 MailService 接口:

java 复制代码
public interface MailService {
    Result<String> getCode(String username, String mailAddress);
}

创建 MailServiceImpl 实现类:

java 复制代码
@Service
public class MailServiceImpl implements MailService {

    @Autowired
    private JavaMailSender mailSender;

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private UserMapper userMapper;

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

    @Value("${spring.mail.code.overtime}")
    private Integer overtime; // 验证码过期时间(分钟)

    @Override
    public Result<String> getCode(String username, String mailAddress) {
        // 校验用户名和邮箱
        if (StringUtils.isBlank(username)) return Result.fail("账号不能为空!");
        if (StringUtils.isBlank(mailAddress)) return Result.fail("邮箱不能为空!");
        
        User user = userMapper.selectUserByUsername(username);
        if (user == null) return Result.fail("账号不存在!");
        if (!user.getEmail().equals(mailAddress)) return Result.fail("输入邮箱和预留邮箱不一致!");

        // 生成验证码
        String verifyCode = redisTemplate.opsForValue().get("MAIL_CODE_" + username);
        if (verifyCode == null) {
            verifyCode = String.valueOf(new Random().nextInt(899999) + 100000);
        }
        redisTemplate.opsForValue().set("MAIL_CODE_" + username, verifyCode, overtime, TimeUnit.MINUTES);

        // 构建邮件内容
        String content = "<html><body>"
                + "您好<br/>"
                + "您的验证码是:" + verifyCode + "<br/>"
                + "在" + overtime + "分钟内有效,请尽快使用。<br/>"
                + "若非本人操作,请忽略此邮件。"
                + "</body></html>";

        try {
            MimeMessage message = mailSender.createMimeMessage();
            MimeMessageHelper helper = new MimeMessageHelper(message, true);
            helper.setFrom(mailUserName);
            helper.setTo(mailAddress);
            helper.setSubject("找回密码验证码");
            helper.setText(content, true);
            mailSender.send(message);
        } catch (MessagingException e) {
            e.printStackTrace();
            return Result.fail("邮件发送失败!");
        }

        return Result.ok("验证码已发送至邮箱:" + mailAddress);
    }
}

五、邮件控制器

java 复制代码
@RestController
@RequestMapping("/mail")
public class MailController {

    @Autowired
    private MailService mailService;

    @GetMapping("/getCode")
    @ApiOperation("获取重置密码验证码")
    public Result<String> getCode(String username, String mailAddress){
        return mailService.getCode(username, mailAddress);
    }
}

六、Redis 验证码存储

  • 使用 StringRedisTemplate 存储验证码:

    • Key: MAIL_CODE_用户名
    • Value: 验证码
    • TTL: spring.mail.code.overtime 分钟
  • 发送邮件后验证码会被存入 Redis,用户提交时进行比对。

相关推荐
葫芦和十三6 小时前
图解 MongoDB 11|慢查询排查闭环:从 Profile 到 explain 的分层路径
后端·mongodb·agent
葫芦和十三9 小时前
图解 MongoDB 09|explain 再读:从 queryPlanner 到 executionStats
后端·mongodb·agent
葫芦和十三9 小时前
图解 MongoDB 10|覆盖查询:让索引把活干完,根本不用回表
后端·mongodb·agent
大鸡腿同学11 小时前
从 CoT 思维链到 ReAct:智能 Agent 到底是怎么 “思考” 的?
后端
IT_陈寒13 小时前
Vite的静态资源打包让我熬夜到三点,这坑千万别跳
前端·人工智能·后端
徐小夕13 小时前
万字拆解 JitWord:企业级实时协同文档底层架构 + 大模型 AI 融合完整实践
前端·vue.js·github
SamDeepThinking13 小时前
高并发场景下,CompletableFuture与ForkJoinPool该如何取舍?
java·后端·面试
学测绘的小杨14 小时前
CompassFusion:一个从 GNSS 到 GNSS/INS 组合导航的独立工程包
python
Asize14 小时前
多模态生图:从 Vite 工程化到前端调用 Qwen Image
javascript·人工智能·后端
java小白小14 小时前
SpringBoot(09):缓存实战——穿透、雪崩、击穿的解决方案
后端