基于SpringBoot实现邮箱找回密码
基于邮箱发送验证码的方式
实现思路
- 用户点击忘记密码
- 用户输入用户名以及邮箱,点击获取验证码
- 后端校验用户名以及邮箱,正确后生成验证码
- 生成的验证码作为value,前缀加用户名为key,放入redis中并设置过期时间
- 用户输入验证码以及新的密码点击保存
- 后端通过前缀+用户名获取验证码,校验验证码的正确性
- 密码修改成功
前端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