基於郵箱發送驗證碼的方式
實現思路
- 用戶點擊忘記密碼
- 用戶輸入用戶名以及郵箱,點擊獲取驗證碼
- 后端校驗用戶名以及郵箱,正確后生成驗證碼
- 生成的驗證碼作為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