之前写了一篇文章 SpringBoot 发送邮件,本文介绍 SpringBoot 找回密码功能。
1、找回密码页面
http://localhost:8080/login?action=lostpass
可以输入用户名或电子邮箱,两者在都是 unique 的字段
2、显示信息
http://localhost:8080/lostpass
如果用户名或邮箱不存在
如果用户名或邮箱存在
3、用户存在,接受邮件
打开qq邮箱,可以看到有一封邮件
如果没收到,可以垃圾箱里有没有
打开邮件,可以看如下
4、显示重置密码页面
http://localhost:8080/resetpass?sid=fea52cfa44c7c11d75e1194986c290fd&account=saysky
具体代码如下
这里主要是介绍邮箱找回密码的功能实现,所以只贴出一些比较关键的代码
1、pom.xml
2、application.properties
3、MailService.java 邮件发送
1、MailRetrieve
2、MailRetrieveRepository
3、MailRetrieveService
4、MailRetrieveServiceImpl
5、LoginController
删掉了多余的代码
6、login.html
找回密码
设置密码
本文地址:https://liuyanzhao.com/7586.html
一、效果图
1、找回密码页面
http://localhost:8080/login?action=lostpass
可以输入用户名或电子邮箱,两者在都是 unique 的字段
2、显示信息
http://localhost:8080/lostpass
如果用户名或邮箱不存在
如果用户名或邮箱存在
3、用户存在,接受邮件
打开qq邮箱,可以看到有一封邮件
如果没收到,可以垃圾箱里有没有
打开邮件,可以看如下
4、显示重置密码页面
http://localhost:8080/resetpass?sid=fea52cfa44c7c11d75e1194986c290fd&account=saysky
具体代码如下
二、邮件发送基本功能实现
这里主要是介绍邮箱找回密码的功能实现,所以只贴出一些比较关键的代码
1、pom.xml
- <!--邮件-->
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-mail</artifactId>
- </dependency>
2、application.properties
- #qq企业邮箱
- spring.mail.host=smtp.exmail.qq.com
- spring.mail.username=master@liuyanzhao.com
- spring.mail.password=密码
- spring.mail.properties.smtp.auth=true
- spring.mail.properties.smtp.starttls.enable=true
- spring.mail.properties.smtp.starttls.required=true
- spring.mail.properties.mail.smtp.ssl.enable=true
3、MailService.java 邮件发送
- package com.liuyanzhao.chuyun.service;
- import ch.qos.logback.classic.Logger;
- import org.slf4j.LoggerFactory;
- 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 javax.mail.MessagingException;
- import javax.mail.internet.MimeMessage;
- import java.io.File;
- /**
- * @author 言曌
- * @date 2018/2/6 下午8:25
- */
- @Service
- public class MailService {
- private final Logger logger = (Logger) LoggerFactory.getLogger(this.getClass());
- @Autowired
- private JavaMailSender sender;
- @Value("${spring.mail.username}")
- private String from;
- /**
- * 发送纯文本的简单邮件
- * @param to
- * @param subject
- * @param content
- */
- public void sendSimpleMail(String to, String subject, String content){
- SimpleMailMessage message = new SimpleMailMessage();
- message.setFrom(from);
- message.setTo(to);
- message.setSubject(subject);
- message.setText(content);
- try {
- sender.send(message);
- logger.info("简单邮件已经发送。");
- } catch (Exception e) {
- logger.error("发送简单邮件时发生异常!", e);
- }
- }
- /**
- * 发送html格式的邮件
- * @param to
- * @param subject
- * @param content
- */
- public void sendHtmlMail(String to, String subject, String content){
- MimeMessage message = sender.createMimeMessage();
- try {
- //true表示需要创建一个multipart message
- MimeMessageHelper helper = new MimeMessageHelper(message, true);
- helper.setFrom(from);
- helper.setTo(to);
- helper.setSubject(subject);
- helper.setText(content, true);
- sender.send(message);
- logger.info("html邮件已经发送。");
- } catch (MessagingException e) {
- logger.error("发送html邮件时发生异常!", e);
- }
- }
- /**
- * 发送带附件的邮件
- * @param to
- * @param subject
- * @param content
- * @param filePath
- */
- public void sendAttachmentsMail(String to, String subject, String content, String filePath){
- MimeMessage message = sender.createMimeMessage();
- try {
- //true表示需要创建一个multipart message
- MimeMessageHelper helper = new MimeMessageHelper(message, true);
- helper.setFrom(from);
- helper.setTo(to);
- helper.setSubject(subject);
- helper.setText(content, true);
- FileSystemResource file = new FileSystemResource(new File(filePath));
- String fileName = filePath.substring(filePath.lastIndexOf(File.separator));
- helper.addAttachment(fileName, file);
- sender.send(message);
- logger.info("带附件的邮件已经发送。");
- } catch (MessagingException e) {
- logger.error("发送带附件的邮件时发生异常!", e);
- }
- }
- /**
- * 发送嵌入静态资源(一般是图片)的邮件
- * @param to
- * @param subject
- * @param content 邮件内容,需要包括一个静态资源的id,比如:<img src=\"cid:rscId01\" >
- * @param rscPath 静态资源路径和文件名
- * @param rscId 静态资源id
- */
- public void sendInlineResourceMail(String to, String subject, String content, String rscPath, String rscId){
- MimeMessage message = sender.createMimeMessage();
- try {
- //true表示需要创建一个multipart message
- MimeMessageHelper helper = new MimeMessageHelper(message, true);
- helper.setFrom(from);
- helper.setTo(to);
- helper.setSubject(subject);
- helper.setText(content, true);
- FileSystemResource res = new FileSystemResource(new File(rscPath));
- helper.addInline(rscId, res);
- sender.send(message);
- logger.info("嵌入静态资源的邮件已经发送。");
- } catch (MessagingException e) {
- logger.error("发送嵌入静态资源的邮件时发生异常!", e);
- }
- }
- }
三、找回密码核心实现
1、MailRetrieve
- package com.liuyanzhao.chuyun.entity;
- import javax.persistence.*;
- /**
- * @author 言曌
- * @date 2018/2/23 上午10:24
- */
- @Entity
- @Table(name = "mail_retrieve")
- public class MailRetrieve{
- private static final long serialVersionUID = -1L;
- @Id
- @GeneratedValue(strategy = GenerationType.AUTO)
- private long id;
- @Column(name = "account", nullable = true, length = 100)
- private String account;
- @Column(name = "sid", nullable = true, length = 255)
- private String sid;
- @Column(name = "out_time", nullable = true, length = 100)
- private long outTime;
- public MailRetrieve() {
- }
- public MailRetrieve(String account, String sid, long outTime) {
- this.account = account;
- this.sid = sid;
- this.outTime = outTime;
- }
- public static long getSerialVersionUID() {
- return serialVersionUID;
- }
- public long getId() {
- return id;
- }
- public void setId(long id) {
- this.id = id;
- }
- public MailRetrieve(String account) {
- this.account = account;
- }
- public String getSid() {
- return sid;
- }
- public void setSid(String sid) {
- this.sid = sid;
- }
- public long getOutTime() {
- return outTime;
- }
- public void setOutTime(long outTime) {
- this.outTime = outTime;
- }
- }
2、MailRetrieveRepository
- package com.liuyanzhao.chuyun.repository;
- import com.liuyanzhao.chuyun.entity.MailRetrieve;
- import org.springframework.data.jpa.repository.JpaRepository;
- /**
- * @author 言曌
- * @date 2018/2/23 上午10:40
- */
- public interface MailRetrieveRepository extends JpaRepository<MailRetrieve, Long> {
- /**
- * 根据账号查找用户
- *
- * @param account
- * @return
- */
- MailRetrieve findByAccount(String account);
- }
3、MailRetrieveService
- package com.liuyanzhao.chuyun.service;
- import com.liuyanzhao.chuyun.vo.ResultVO;
- /**
- * 邮件找回密码Service
- *
- * @author 言曌
- * @date 2018/2/23 上午10:27
- */
- public interface MailRetrieveService {
- /**
- * 找回密码
- *
- * @param basePath
- * @param account
- * @return
- */
- public ResultVO generateMailUrl(String basePath, String account);
- /**
- * 邮件找回密码URL校验
- * @param sid
- * @param account
- * @return
- */
- public ResultVO verifyMailUrl(String sid, String account);
- }
4、MailRetrieveServiceImpl
- package com.liuyanzhao.chuyun.service.impl;
- import com.liuyanzhao.chuyun.entity.MailRetrieve;
- import com.liuyanzhao.chuyun.entity.User;
- import com.liuyanzhao.chuyun.repository.MailRetrieveRepository;
- import com.liuyanzhao.chuyun.repository.UserRepository;
- import com.liuyanzhao.chuyun.service.MailRetrieveService;
- import com.liuyanzhao.chuyun.util.MD5Util;
- import com.liuyanzhao.chuyun.util.RandomUtil;
- import com.liuyanzhao.chuyun.vo.ResultVO;
- import com.sun.jmx.snmp.Timestamp;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.stereotype.Service;
- /**
- * @author 言曌
- * @date 2018/2/23 上午10:30
- */
- @Service
- public class MailRetrieveServiceImpl implements MailRetrieveService {
- @Autowired
- private UserRepository userRepository;
- @Autowired
- private MailRetrieveRepository mailRetrieveRepository;
- /**
- * 生成链接
- * @param basePath
- * @param account
- * @return
- */
- @Override
- public ResultVO generateMailUrl(String basePath, String account) {
- User user = userRepository.findByUsername(account);
- ResultVO resultVO = new ResultVO();
- //用户名不存在
- if (user == null) {
- resultVO.setCode("001");
- resultVO.setMsg("用户不存在");
- } else {
- //生成邮件URL唯一地址
- String key = RandomUtil.getRandom(6) + "";
- Timestamp outDate = new Timestamp(System.currentTimeMillis() + (long) (30 * 60 * 1000));//30分钟后过期
- long outtimes = outDate.getSysUpTime();
- String sid = account + "&" + key + "&" + outtimes;
- MailRetrieve mailRetrieve = new MailRetrieve(account, MD5Util.encode(sid), outtimes);
- MailRetrieve findMailRetrieve = mailRetrieveRepository.findByAccount(account);
- if (findMailRetrieve != null) {
- mailRetrieveRepository.delete(findMailRetrieve);
- }
- mailRetrieveRepository.save(mailRetrieve);
- resultVO.setCode("005");
- resultVO.setMsg("邮件重置密码");
- resultVO.setData(basePath + "resetpass?sid=" + MD5Util.encode(sid) + "&username=" + account);
- }
- return resultVO;
- }
- /**
- * 验证链接
- * @param sid
- * @param username
- * @return
- */
- @Override
- public ResultVO verifyMailUrl(String sid, String username) {
- ResultVO resultVO = new ResultVO();
- MailRetrieve mailRetrieve = mailRetrieveRepository.findByAccount(username);
- if(mailRetrieve != null) {
- long outTime = mailRetrieve.getOutTime();
- Timestamp outDate = new Timestamp(System.currentTimeMillis());
- long nowTime = outDate.getSysUpTime();
- System.out.println("nowTime:" + nowTime);
- if (outTime <= nowTime) {
- resultVO.setCode("006");
- resultVO.setMsg("邮件已经过期!");
- } else if ("".equals(sid)) {
- resultVO.setCode("007");
- resultVO.setMsg("sid不完整!");
- } else if (!sid.equals(mailRetrieve.getSid())) {
- resultVO.setCode("008");
- resultVO.setMsg("sid错误!");
- } else {
- resultVO.setCode("000");
- resultVO.setMsg("验证成功!");
- }
- } else {
- //account 对应的用户不存在
- resultVO.setCode("002");
- resultVO.setMsg("链接无效!");
- }
- return resultVO;
- }
- }
5、LoginController
- package com.liuyanzhao.chuyun.controller;
- import com.liuyanzhao.chuyun.entity.Authority;
- import com.liuyanzhao.chuyun.entity.User;
- import com.liuyanzhao.chuyun.repository.MailRetrieveRepository;
- import com.liuyanzhao.chuyun.repository.UserRepository;
- import com.liuyanzhao.chuyun.service.AuthorityService;
- import com.liuyanzhao.chuyun.service.MailRetrieveService;
- import com.liuyanzhao.chuyun.service.MailService;
- import com.liuyanzhao.chuyun.service.UserService;
- import com.liuyanzhao.chuyun.util.BCryptUtil;
- import com.liuyanzhao.chuyun.util.QQUtil;
- import com.liuyanzhao.chuyun.util.RegexUtil;
- import com.liuyanzhao.chuyun.vo.ResultVO;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.stereotype.Controller;
- import org.springframework.web.bind.annotation.GetMapping;
- import org.springframework.web.bind.annotation.PostMapping;
- import org.springframework.web.bind.annotation.RequestParam;
- import org.springframework.web.servlet.ModelAndView;
- import javax.servlet.http.HttpServletRequest;
- /**
- * @author 言曌
- * @date 2018/2/23 下午5:31
- */
- @Controller
- public class LoginController {
- @Autowired
- private UserRepository userRepository;
- @Autowired
- private UserService userService;
- @Autowired
- private MailRetrieveService mailRetrieveService;
- @Autowired
- private MailService mailService;
- /**
- * 登录页面显示
- *
- * @return
- */
- @GetMapping("/login")
- public String login(@RequestParam(value = "action", required = false) String action) {
- //登录
- if (action == null) {
- return "login";
- }
- //注册
- else if ("register".equals(action)) {
- return "forward:/register";
- }
- //忘记密码
- else if ("lostpass".equals(action)) {
- return "forward:/lostpass";
- }
- //设置密码
- else if ("resetpass".equals(action)) {
- return "forward:/resetpass";
- }
- //退出登录
- else if ("logout".equals(action)) {
- return "forward:/logout";
- }
- //其他
- else {
- return "login";
- }
- }
- /**
- * 忘记密码页面显示
- *
- * @return
- */
- @GetMapping(value = "/lostpass")
- public ModelAndView forget() {
- ModelAndView modelAndView = new ModelAndView("login");
- modelAndView.addObject("action", "lostpass");
- modelAndView.addObject("message", "请输入您的用户名或电子邮箱地址。您会收到一封包含创建新密码链接的电子邮件。");
- return modelAndView;
- }
- /**
- * 忘记密码提交,然后跳转登录页面
- *
- * @param request
- * @param account
- * @return
- */
- @PostMapping(value = "/lostpass")
- public ModelAndView forgetUser(HttpServletRequest request, String account) {
- ModelAndView modelAndView = new ModelAndView("login");
- String username = "";
- //1、获取用户名 username
- if (RegexUtil.isEmail(account)) {
- User user = userRepository.findByEmail(account);
- if (user != null) {
- username = user.getUsername();
- }
- } else {
- //account 是用户名
- username = account;
- }
- User user = userRepository.findByUsername(username);
- //2、判断用户是否存在
- if(user != null) {
- //用户存在
- //2.1、生成链接
- String path = request.getContextPath();
- String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path + "/";
- ResultVO resultVO = mailRetrieveService.generateMailUrl(basePath, username);
- String mailUrl = resultVO.getData().toString();
- //2.2、发送邮件
- String receiver = user.getEmail();//接收者
- String subject = "[Spring Blog]密码重置";//邮件主题(标题)
- StringBuilder content = new StringBuilder();
- content.append("有人为以下账号请求了密码重置:<br/><br/>");
- content.append("<a href=" + basePath + " target=\"_blank\">" + basePath + "</a><br/><br/>");
- content.append("用户名:" + username + "<br/><br/>");
- content.append("若这不是您本人要求的,请忽略本邮件,一切如常。<br/><br/>");
- content.append("要重置您的密码,请打开下面的链接(如果邮箱对链接拦截,请复制链接在地址栏中打开,30分钟内有效):<br/><br/>");
- content.append("<a href=" + mailUrl + " target=\"_blank\">" + mailUrl + "</a><br/><br/>");
- mailService.sendHtmlMail(receiver, subject, content.toString());
- //2.3、显示登录页面和提示信息
- modelAndView.addObject("checkmail", "请在您的电子邮箱中检查确认链接。");
- } else {
- //用户不存在
- //2.1、 显示找回密码页面,和错误信息
- modelAndView.addObject("error","用户名或邮箱不存在!");
- modelAndView.addObject("action","lostpass");
- }
- return modelAndView;
- }
- /**
- * 重置密码验证
- * 验证通过,显示修改密码页面
- *
- * @param sid
- * @param username
- * @return
- */
- @GetMapping(value = "/resetpass")
- public ModelAndView verifyMail(String sid, String username) {
- ModelAndView modelAndView = new ModelAndView("login");
- ResultVO resultVO = mailRetrieveService.verifyMailUrl(sid, username);
- //验证通过,显示设置密码页面
- if ("000".equals(resultVO.getCode())) {
- modelAndView.addObject("action", "resetpass");
- modelAndView.addObject("username", username);
- modelAndView.addObject("sid", sid);
- } else {
- //链接无效,显示找回密码页面
- modelAndView.addObject("action", "lostpass");
- //显示错误信息
- modelAndView.addObject("error", resultVO.getMsg());
- }
- return modelAndView;
- }
- /**
- * 重置密码提交
- *
- * @param sid 根据用户sid来查询用户名
- * @param password
- * @return
- */
- @PostMapping(value = "/resetpass")
- public ModelAndView resetPass(String sid, String username, String password) {
- ModelAndView modelAndView = new ModelAndView("login");
- ResultVO resultVO = mailRetrieveService.verifyMailUrl(sid, username);
- //如果验证通过(防止用户自定义表单,再次验证)
- if ("000".equals(resultVO.getCode())) {
- //修改密码
- User user = userService.getUserByUsername(username);
- user.setPassword(BCryptUtil.encode(password));
- userService.updateUser(user);
- }
- //验证失败
- else {
- //显示找回密码页面
- modelAndView.addObject("action", "lostpass");
- //显示错误信息
- modelAndView.addObject("error", "用户名或sid不正确");
- }
- return modelAndView;
- }
- }
删掉了多余的代码
6、login.html
找回密码
- <div id="lostpass-block" th:case="lostpass">
- <p class="message" th:if="${message}" th:text="${message}"></p>
- <div id="login_error" th:if="${error}" >
- <span th:text="${error}"></span>请重新申请。
- </div>
- <form name="forgetForm" th:action="@{/lostpass}" id="forgetForm" method="post">
- <p>
- <label for="account">用户名或电子邮件地址<br/>
- <input type="text" name="account" id="account" class="input" value="" size="20"/>
- </label>
- </p>
- <br>
- <p class="submit">
- <input type="submit" value="获取新密码"/>
- </p>
- </form>
- </div>
设置密码
- <div id="resetpass-block" th:case="resetpass">
- <p class="message reset-pass">请在下方输入您的新密码。</p>
- <div id="login_error" th:if="${message}" th:text="${message}"></div>
- <form name="resetpassform" id="resetpassform" th:action="@{/resetpass}"
- method="post" autocomplete="off">
- <p>
- <label for="username"><strong>新密码</strong><br/>
- <input type="text" name="password" id="password" class="input" value="" size="20"/>
- </label>
- </p>
- <p class="description indicator-hint" style="margin-top: 10px;">正在为用户名为 <strong th:text="${username}"></strong> 的用户重置密码</p>
- <br class="clear">
- <input type="hidden" name="username" th:value="${username}">
- <input type="hidden" name="sid" th:value="${sid}">
- <p class="submit">
- <input type="submit" value="重置密码">
- </p>
- </form>
- </div>
本文地址:https://liuyanzhao.com/7586.html
2019年01月09日 21:54:42
aaaaaaaaaaa啊
2019年01月09日 21:55:38
6啊