基于SpringBoot实现邮箱找回密码

本文涉及的产品
云数据库 Tair(兼容Redis),内存型 2GB
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
简介: 基于SpringBoot实现邮箱找回密码

基于邮箱发送验证码的方式

》》文章末尾有demo的git地址供下载参考

实现思路

  1. 用户点击忘记密码
  2. 用户输入用户名以及邮箱,点击获取验证码
  3. 后端校验用户名以及邮箱,正确后生成验证码
  4. 生成的验证码作为value,前缀加用户名为key,放入redis中并设置过期时间
  5. 用户输入验证码以及新的密码点击保存
  6. 后端通过前缀+用户名获取验证码,校验验证码的正确性
  7. 密码修改成功

前端UI页面

图为ProcessOn所画,可作为借鉴。也可设计为先验证用户名然后再进入改密码页面。

发件邮箱要求

作者使用的是163邮箱(其他邮箱也基本一致),需要设置POP3/SMTP/IMAP

开启

  • IMAP/SMTP服务
  • POP3/SMTP服务
    开启会让设置授权码,授权码要记牢!后面需要写在配置里

再往下翻有163 SMTP服务器地址 等下需要配置在application.yml中

各家邮箱大同小异,企业邮箱的话应该是可以直接用的,不用开启。

代码实现

1. 依赖

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

2. application.yml配置

# 配置邮箱服务器,账号密码等
spring:
  mail:
    host: smtp.163.com
    username: xxxxxx@163.com
    password: ERBDGXLVJAQMWMDI(授权码)

3. 获取验证码controller

import com.clisoft.srmsbackend.common.response.ServerResponse;
import com.clisoft.srmsbackend.service.IMailService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
 * 邮箱业务 前端控制器
 * @author zzw
 * @date 2022-01-14
 */
@Api(tags = "邮箱业务管理")
@RestController
@RequestMapping("/mail")
public class MailController {
    @Autowired
    private IMailService mailService;
    /**
     * 获取重置密码的验证码
     */
    @ApiOperation(value = "获取重置密码的验证码", notes = "获取重置密码的验证码", httpMethod = "GET")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "staffNumber", value = "用户编号(账号)", required = true, paramType = "form"),
            @ApiImplicitParam(name = "mailAddress", value = "邮箱地址", required = true, paramType = "form"),
    })
    @GetMapping("/getCode")
    public ServerResponse getCode(String staffNumber,String mailAddress){
        return mailService.getCode(staffNumber,mailAddress);
    }
}

4. ServiceImpl层

import com.clisoft.srmsbackend.common.constant.Constants;
import com.clisoft.srmsbackend.common.response.ServerResponse;
import com.clisoft.srmsbackend.config.MailCodeConfig;
import com.clisoft.srmsbackend.dao.entity.PersStaff;
import com.clisoft.srmsbackend.dao.mapper.PersStaffMapper;
import com.clisoft.srmsbackend.framework.redis.RedisCache;
import com.clisoft.srmsbackend.service.IMailService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Service;
import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import java.util.Random;
import java.util.concurrent.TimeUnit;
/**
 * 邮箱业务 实现类
 *
 * @author zzw
 * @date 2022-01-14
 */
@Service
public class MailServiceImpl implements IMailService {
    @Autowired
    private PersStaffMapper persStaffMapper;
    @Autowired
    private MailCodeConfig mailCodeConfig;
    @Autowired
    private RedisCache redisCache;
    @Autowired
    private JavaMailSender mailSender;
    @Value("${spring.mail.username}")
    private String mailUserName;
    /**
     * 获取重置密码的验证码
     *
     * @param staffNumber 用户账号
     * @param mailAddress 用户邮箱
     * @return
     */
    @Override
    public ServerResponse getCode(String staffNumber, String mailAddress) {
        // 非空校验
        if (null == staffNumber || "".equals(staffNumber)) return ServerResponse.createByErrorMessage("账号不能为空!");
        if (null == mailAddress || "".equals(mailAddress)) return ServerResponse.createByErrorMessage("邮箱不能为空!");
        // 账号存在校验
        PersStaff persStaff = persStaffMapper.selectPersStaffByStaffNumber(staffNumber);
        if (null == persStaff) return ServerResponse.createBySuccessMessage("账号不存在!");
        if (!persStaff.getEmail().equals(mailAddress)) return ServerResponse.createByErrorMessage("输入邮箱和预留邮箱不一致!");
        String verifyCode = redisCache.getCacheObject(Constants.MAIL_CODE_KEY + staffNumber);
        if (verifyCode == null) {
            verifyCode = String.valueOf(new Random().nextInt(899999) + 100000);//生成短信验证码
        }
        Integer overtime = mailCodeConfig.getOvertime(); // 过期时间
        // 验证码存入redis并设置过期时间
        redisCache.setCacheObject(Constants.MAIL_CODE_KEY + staffNumber, verifyCode, overtime, TimeUnit.MINUTES);
        // 编写邮箱内容
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("<html><head><title></title></head><body>");
        stringBuilder.append("您好<br/>");
        stringBuilder.append("您的验证码是:").append(verifyCode).append("<br/>");
        stringBuilder.append("您可以复制此验证码并返回至科研管理系统找回密码页面,以验证您的邮箱。<br/>");
        stringBuilder.append("此验证码只能使用一次,在");
        stringBuilder.append(overtime.toString());
        stringBuilder.append("分钟内有效。验证成功则自动失效。<br/>");
        stringBuilder.append("如果您没有进行上述操作,请忽略此邮件。");
        MimeMessage mimeMessage = mailSender.createMimeMessage();
        // 发件配置并发送邮件
        try {
            MimeMessageHelper mimeMessageHelper = new MimeMessageHelper(mimeMessage, true);
            //这里只是设置username 并没有设置host和password,因为host和password在springboot启动创建JavaMailSender实例的时候已经读取了
            mimeMessageHelper.setFrom(mailUserName);
            // 用户的邮箱地址
            mimeMessageHelper.setTo(mailAddress);
            // 邮件的标题
            mimeMessage.setSubject("邮箱验证-科研管理系统");
            // 上面所拼接的邮件内容
            mimeMessageHelper.setText(stringBuilder.toString(), true);
            mailSender.send(mimeMessage);
        } catch (MessagingException e) {
            e.printStackTrace();
        }
        return ServerResponse.createBySuccessMessage("获取验证码成功,请查看移步您的邮箱" + mailAddress + "查看验证码!");
    }
}

5. 修改密码controller

/**
     * 验证码重置密码
     */
    @ApiOperation(value = "验证码重置密码", notes = "验证码重置密码", httpMethod = "POST")
    @PostMapping("/codeUpdatePwd")
    public ServerResponse codeUpdatePwd(@RequestBody CodeUpdatePwdVo codeUpdatePwdVo){
        return persStaffService.codeUpdatePwd(codeUpdatePwdVo);
    }

6. 修改密码ServiceImpl

 /**
     * 验证码重置密码
     *
     * @param codeUpdatePwdVo
     * @return
     */
    @Override
    public ServerResponse codeUpdatePwd(CodeUpdatePwdVo codeUpdatePwdVo) {
        String staffNumber = codeUpdatePwdVo.getStaffNumber();
        String code = codeUpdatePwdVo.getCode();
        String loginPassword = codeUpdatePwdVo.getLoginPassword();
        // 非空校验
        if (null == staffNumber || "".equals(staffNumber)) return ServerResponse.createByErrorMessage("账号不能为空!");
        if (null == code || "".equals(code)) return ServerResponse.createByErrorMessage("验证码不能为空!");
        if (null == loginPassword || "".equals(loginPassword)) return ServerResponse.createByErrorMessage("密码不能为空!");
        // 账号存在校验
        PersStaff persStaff = persStaffMapper.selectPersStaffByStaffNumber(staffNumber);
        if (null == persStaff) return ServerResponse.createBySuccessMessage("账号不存在!");
        // 验证码过期校验
        String cacheCode = redisCache.getCacheObject(Constants.MAIL_CODE_KEY + staffNumber); // 获取缓存中该账号的验证码
        if (cacheCode == null) {
            return ServerResponse.createByErrorMessage("验证码已过期,请重新获取!");
        }
        // 验证码正确性校验
        if (!cacheCode.equals(code)) {
            return ServerResponse.createByErrorMessage("验证码错误!");
        }
        // 修改密码
        int result = 0;
        try {
            result = persStaffMapper.updatePwdByStaffNumber(staffNumber, PasswordStorageUtil.createHash(codeUpdatePwdVo.getLoginPassword()));
        } catch (PasswordStorageUtil.CannotPerformOperationException e) {
            return ServerResponse.createByErrorMessage("密码加密时发生错误");
        }
        if (result > 0) {
            // 将验证码过期
            redisCache.expire(Constants.MAIL_CODE_KEY + staffNumber, 0);
            return ServerResponse.createBySuccessMessage("密码充值成功!请牢记您的密码!");
        }
        return ServerResponse.createByErrorMessage("未知错误,密码修改失败,请重试!");
    }

7. 效果图

8. 问题

  • 作者没有遇到什么问题,如有问题可留言讨论。

demo地址:https://gitee.com/lm_8692769/email-reset-password-demo


相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
6月前
|
druid Java 数据库
druid+springboot加解密Druid链接池配置加密密码链接数据库
druid+springboot加解密Druid链接池配置加密密码链接数据库
326 0
|
6月前
|
Java 数据库 数据安全/隐私保护
【SpringBoot】Validator组件+自定义约束注解实现手机号码校验和密码格式限制
【SpringBoot】Validator组件+自定义约束注解实现手机号码校验和密码格式限制
620 1
|
1月前
|
Java 数据安全/隐私保护 Spring
springboot实现邮箱发送(激活码)功能
本文介绍了如何在Spring Boot应用中配置和使用邮箱发送功能,包括开启邮箱的SMTP服务、添加Spring Boot邮件发送依赖、配置application.properties文件,以及编写邮件发送的代码实现。
71 2
springboot实现邮箱发送(激活码)功能
|
1月前
|
NoSQL Java Redis
shiro学习四:使用springboot整合shiro,正常的企业级后端开发shiro认证鉴权流程。使用redis做token的过滤。md5做密码的加密。
这篇文章介绍了如何使用Spring Boot整合Apache Shiro框架进行后端开发,包括认证和授权流程,并使用Redis存储Token以及MD5加密用户密码。
30 0
shiro学习四:使用springboot整合shiro,正常的企业级后端开发shiro认证鉴权流程。使用redis做token的过滤。md5做密码的加密。
|
6月前
|
安全 Java 关系型数据库
springboot+jsp商务安全邮箱(源码+文档)
一款基于SpringBoot的商务安全邮箱项目,包括收件箱、草稿箱、已发送、垃圾邮箱和删除邮件等功能。项目源码与部署详情可联系作者,使用技术有Java、SpringBoot、Mybatis、JDK1.8和MySQL等。
|
5月前
|
Java 数据安全/隐私保护
Springboot实现发送邮箱
Springboot实现发送邮箱
51 0
|
存储 Java 数据库
java spring boot 数据库密码解密
java spring boot 数据库密码解密
|
JSON 算法 Java
Spring boot框架 JWT实现用户账户密码登录验证
Spring boot框架 JWT实现用户账户密码登录验证
|
6月前
|
Java 数据库 数据安全/隐私保护
SpringBoot项目使用jasypt加解密的方法加密数据库密码
SpringBoot项目使用jasypt加解密的方法加密数据库密码
117 0
|
6月前
|
安全 架构师 Java
SpringBoot【集成 jasypt】实现配置信息自定义加解密(自定义的属性探测和密码解析器)
SpringBoot【集成 jasypt】实现配置信息自定义加解密(自定义的属性探测和密码解析器)
825 0