Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >[Springboot]发送邮件、重置密码业务实战

[Springboot]发送邮件、重置密码业务实战

作者头像
蛮三刀酱
发布于 2019-03-26 03:39:52
发布于 2019-03-26 03:39:52
79900
代码可运行
举报
运行总次数:0
代码可运行

前言

忘记密码并通过邮件重置密码是一个常见的业务需求,在开发我的个人小项目过程中,也需要用到这个业务,今天就给大家带来一个业务实战。

开发环境

  • springboot:1.5.16.RELEASE

业务流程

根据controller中函数分为两个部分:

  1. 用户申请重置邮件:
  • 用户在页面中输入邮箱
  • 服务器检查是否允许重置(邮箱所指向用户是否存在,重置是否过于频繁,重置是否到达日请求上限)
  • 验证通过后,想validate表写入申请记录,包含token,用户邮箱和id
  • 发送邮件(包含带有token的链接)
  • 用户点击邮件内连接
  • 跳转到新密码输入网页
  • 提交重置密码请求(POST中包含token,新密码)
  1. 用户重置密码
  • 服务器验证token(token是否过期,该用户是否发起过其它新token)
  • 通过validate表记录查找用户id,修改用户密码

实战

  1. pom.xml添加email依赖
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<!--邮件: email-->
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
  1. 添加pm_validate表结构

其中reset_token由UUID生成,type默认为resetPassword(方便以后新增需求),user_id为用户表用户id

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
-- ----------------------------
-- Table structure for pm_validate
-- ----------------------------
DROP TABLE IF EXISTS `pm_validate`;
CREATE TABLE `pm_validate` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` int(11) NOT NULL,
  `email` varchar(40) NOT NULL,
  `reset_token` varchar(40) NOT NULL,
  `type` varchar(20) NOT NULL,
  `gmt_create` datetime DEFAULT NULL,
  `gmt_modified` datetime DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

生成或编写对应pojo和mapper。,由于我使用了mybatis-generator插件,需要运行插件生成对应pojo和mapper。

  1. 修改application.properties,添加邮箱配置
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
# 发送邮件配置
spring.mail.host=smtp.gmail.com
spring.mail.username=xxxxxx@gmail.com
spring.mail.password=xxxxxxx
spring.mail.properties.mail.smtp.auth=true
spring.mail.properties.mail.smtp.starttls.enable=true
spring.mail.properties.mail.smtp.starttls.required=true
  1. 编写controller和service
  • ValidateController
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@RestController
@RequestMapping(value = "/validate")
public class ValidateController {

    @Autowired
    private ValidateService validateService;

    @Autowired
    private UserService userService;

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

    /**
     * 发送忘记密码邮件请求,每日申请次数不超过5次,每次申请间隔不低于1分钟
     * @param email
     * @param request
     * @return
     */
    @ApiOperation(value = "发送忘记密码邮件", notes = "发送忘记密码邮件")
    @RequestMapping(value = "/sendValidationEmail", method = {RequestMethod.POST})
    public ResponseData<String> sendValidationEmail(@ApiParam("邮箱地址") @RequestParam("email") String email,
                                               HttpServletRequest request){
        ResponseData<String> responseData = new ResponseData<>();
        List<User> users = userService.findUserByEmail(email);
        if (users == null){
            responseData.jsonFill(2, "该邮箱所属用户不存在", null);
        }else {
            if (validateService.sendValidateLimitation(email, 5,1)){
                // 若允许重置密码,则在pm_validate表中插入一行数据,带有token
                Validate validate = new Validate();
                validateService.insertNewResetRecord(validate, users.get(0), UUID.randomUUID().toString());
                // 设置邮件内容
                String appUrl = request.getScheme() + "://" + request.getServerName();
                SimpleMailMessage passwordResetEmail = new SimpleMailMessage();
                passwordResetEmail.setFrom(from);
                passwordResetEmail.setTo(email);
                passwordResetEmail.setSubject("【电商价格监控】忘记密码");
                passwordResetEmail.setText("您正在申请重置密码,请点击此链接重置密码: \n" + appUrl + "/validate/reset?token=" + validate.getResetToken());
                validateService.sendPasswordResetEmail(passwordResetEmail);
                responseData.jsonFill(1, null, null);
            }else {
                responseData.jsonFill(2,"操作过于频繁,请稍后再试!",null);
            }
        }
        return responseData;
    }

    /**
     * 将url的token和数据库里的token匹配,成功后便可修改密码,token有效期为60分钟
     * @param token
     * @param password
     * @param confirmPassword
     * @return
     */
    @ApiOperation(value = "重置密码", notes = "重置密码")
    @RequestMapping(value = "/resetPassword", method = RequestMethod.POST)
    public ResponseData<String> resetPassword(@ApiParam("token") @RequestParam("token") String token,
                                              @ApiParam("密码") @RequestParam("password") String password,
                                              @ApiParam("密码确认") @RequestParam("confirmPassword") String confirmPassword){
        ResponseData<String> responseData = new ResponseData<>();
        // 通过token找到validate记录
        List<Validate> validates = validateService.findUserByResetToken(token);
        if (validates == null){
            responseData.jsonFill(2,"该重置请求不存在",null);
        }else {
            Validate validate = validates.get(0);
            if (validateService.validateLimitation(validate.getEmail(), Long.MAX_VALUE, 60, token)){
                Integer userId = validate.getUserId();
                if (password.equals(confirmPassword)) {
                    userService.updatePassword(password, userId);
                    responseData.jsonFill(1, null,null);
                }else {
                    responseData.jsonFill(2,"确认密码和密码不一致,请重新输入", null);
                }
            }else {
                responseData.jsonFill(2,"该链接失效",null);
            }
        }
        return responseData;
    }
}
  • ValidateService
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public interface ValidateService {
    void sendPasswordResetEmail(SimpleMailMessage email);
    int insertNewResetRecord(Validate validate, User users, String token);
    List<Validate> findUserByResetToken(String resetToken);
    boolean validateLimitation(String email, long requestPerDay, long interval, String token);
    boolean sendValidateLimitation(String email, long requestPerDay, long interval);
}
  • ValidateServiceImpl
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Service
public class ValidateServiceImpl implements ValidateService {

    @Autowired
    private JavaMailSender javaMailSender;

    @Autowired
    private ValidateMapper validateMapper;

    /**
     * 发送邮件:@Async进行异步调用发送邮件接口
     * @param email
     */
    @Override
    @Async
    public void sendPasswordResetEmail(SimpleMailMessage email){
        javaMailSender.send(email);
    }

    /**
     * 在pm_validate表中插入一条validate记录,userid,email属性来自pm_user表,token由UUID生成
     * @param validate
     * @param users
     * @param token
     * @return
     */
    @Override
    public int insertNewResetRecord(Validate validate, User users, String token){
        validate.setUserId(users.getId());
        validate.setEmail(users.getEmail());
        validate.setResetToken(token);
        validate.setType("passwordReset");
        validate.setGmtCreate(new Date());
        validate.setGmtModified(new Date());
        return validateMapper.insert(validate);
    }

    /**
     * pm_validate表中,通过token查找重置申请记录
     * @param token
     * @return
     */
    @Override
    public List<Validate> findUserByResetToken(String token){
        ValidateExample validateExample = new ValidateExample();
        ValidateExample.Criteria criteria = validateExample.createCriteria();
        criteria.andResetTokenEqualTo(token);
        return validateMapper.selectByExample(validateExample);
    }

    /**
     * 验证是否发送重置邮件:每个email的重置密码每日请求上限为requestPerDay次,与上一次的请求时间间隔为interval分钟。
     * @param email
     * @param requestPerDay
     * @param interval
     * @return
     */
    @Override
    public boolean sendValidateLimitation(String email, long requestPerDay, long interval){
        ValidateExample validateExample = new ValidateExample();
        ValidateExample.Criteria criteria= validateExample.createCriteria();
        criteria.andEmailEqualTo(email);
        List<Validate> validates = validateMapper.selectByExample(validateExample);
        // 若查无记录,意味着第一次申请,直接放行
        if (validates.isEmpty()) {
            return true;
        }
        // 有记录,则判定是否频繁申请以及是否达到日均请求上线
        long countTodayValidation = validates.stream().filter(x->DateUtils.isSameDay(x.getGmtModified(), new Date())).count();
        Optional validate = validates.stream().map(Validate::getGmtModified).max(Date::compareTo);
        Date dateOfLastRequest = new Date();
        if (validate.isPresent()) dateOfLastRequest = (Date) validate.get();
        long intervalForLastRequest = new Date().getTime() - dateOfLastRequest.getTime();

        return countTodayValidation <= requestPerDay && intervalForLastRequest >= interval * 60 * 1000;
    }

    /**
     * 验证连接是否失效:链接有两种情况失效 1.超时 2.最近请求的一次链接自动覆盖之前的链接(待看代码)
     * @param email
     * @param requestPerDay
     * @param interval
     * @return
     */
    @Override
    public boolean validateLimitation(String email, long requestPerDay, long interval, String token){
        ValidateExample validateExample = new ValidateExample();
        ValidateExample.Criteria criteria= validateExample.createCriteria();
        criteria.andEmailEqualTo(email);
        List<Validate> validates = validateMapper.selectByExample(validateExample);
        // 有记录才会调用该函数,只需判断是否超时
        Optional validate = validates.stream().map(Validate::getGmtModified).max(Date::compareTo);
        Date dateOfLastRequest = new Date();
        if (validate.isPresent()) dateOfLastRequest = (Date) validate.get();
        long intervalForLastRequest = new Date().getTime() - dateOfLastRequest.getTime();

        Optional lastRequestToken = validates.stream().filter(x-> x.getResetToken().equals(token)).map(Validate::getGmtModified).findAny();
        Date dateOfLastRequestToken = new Date();
        if (lastRequestToken.isPresent()) {
            dateOfLastRequestToken = (Date) lastRequestToken.get();
        }
        return intervalForLastRequest <= interval * 60 * 1000 && dateOfLastRequest == dateOfLastRequestToken;
    }
}

结语

如上实现了整个重置密码流程,前端网页自行设计实现。

关注我

我是蛮三刀把刀,目前为后台开发工程师。主要关注后台开发,网络安全Python爬虫等技术。

来微信和我聊聊:yangzd1102

Github:https://github.com/qqxx6661

原创博客主要内容

  • 笔试面试复习知识点手册
  • Leetcode算法题解析(前150题)
  • 剑指offer算法题解析
  • Python爬虫相关技术分析和实战
  • 后台开发相关技术分析和实战
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2019年02月15日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Springboot实战:发送邮件/重置密码业务
忘记密码并通过邮件重置密码是一个常见的业务需求,在开发我的个人小项目过程中,也需要用到这个业务,今天就给大家带来一个业务实战。
Rude3Knife的公众号
2019/08/07
1.6K0
SpringBoot实现通过邮箱找回密码功能
之前在大学里面做项目的时候碰到修改密码那一块的,自己当时都是做的很简单的逻辑,也想过怎么通过邮箱或者手机号这种进一步验证身份来修改密码,但是自己当时太菜了,也没怎么好好钻研,所以就一直没尝试过那样的功能,但是这次公司项目里面可能会用到,于是自己找了找教程看了看,发现实现起来不难,毕竟别人已经把轮子已经造好了,但是其中还是遇到了一些问题,还是费了不少时间.希望这篇教程能过对你有所帮助.
萌萌哒的瓤瓤
2021/01/13
2.5K0
SpringBoot实现通过邮箱找回密码功能
Spring Cloud 2.x系列之springboot发送邮件
虽然现在短信验证已经最流行也是最常用的验证方式;但是邮件验证还是必不可少,依然是网站的必备功能之一。什么注册验证,忘记密码或者是给用户发送营销信息都是可以使用邮件发送功能的。最早期使用JavaMail的相关api来进行发送邮件的功能开发,后来spring整合了JavaMail的相关api推出了JavaMailSender更加简化了邮件发送的代码编写,现在springboot对此进行了封装就有了现在的spring-boot-starter-mail。
BUG弄潮儿
2022/06/30
4480
Spring Cloud 2.x系列之springboot发送邮件
记录一下若依集成发送邮件
想在博客里评论时添加邮件提醒!!! 说干就干!!! 1、添加pom依赖 <!-- 邮件依赖 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-mail</artifactId> </dependency> 复制 2、配置yml # Spring配置 spring: # 邮箱配置 mail: host: smtp.qq.com us
裴大头
2022/01/17
1.4K0
记录一下若依集成发送邮件
SpringBoot 教程之发送邮件
Spring Boot 收发邮件最简便方式是通过 spring-boot-starter-mail。
静默虚空
2022/03/23
1.6K0
SpringBoot 教程之发送邮件
SpringBoot优雅地发送邮件
消息通知的形式也有很多,比如:短信、邮件、app推送等,本文主要给大家描述一下邮件通知的形式,因为邮件相比较其他通知渠道更方便实用(免费),除了简单文本邮件(已经满足大多数情形),本文还会重点说一下集成Thymeleaf模版引擎,使用HTML的形式发送邮件,尽管HTML内容不是标准化的消息格式,但是许多邮件客户端至少支持标记语言的子集,这种方式相比较纯文本展现形式更加友好。
程序员小明
2019/09/17
7030
SpringBoot-19-之发送邮件
零、准备工作 <!--发送邮件的依赖--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-mail</artifactId> </dependency> spring: mail: host: smtp.126.com username: toly1994@126.com password: 你的密码--注意不是登陆密
张风捷特烈
2018/09/29
4410
SpringBoot-19-之发送邮件
SpringMVC通过邮件找回密码功能的实现
1、最近开发一个系统,有个需求就是,忘记密码后通过邮箱找回。现在的系统在注册的时候都会强制输入邮箱,其一目的就是 通过邮件绑定找回,可以进行密码找回。通过java发送邮件的功能我就不说了,重点讲找回密码。
用户7999227
2021/09/23
8550
SpringBoot发送邮件
pom <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-mail</artifactId> </dependency> 发送简单文本邮件 @Autowired JavaMailSenderImpl mailSender; @Async public void SendEm
鱼找水需要时间
2023/02/16
2480
SpringBoot发送邮件
springboot (七) 邮件发送功能
介绍springboot的邮件发送。 由于很简单就没有分出server和imp之类,只是在controller简单写个方法进行测试。 首先pom文件加入spring-boot-starter-mail
IT架构圈
2018/06/01
6470
如何实现通过邮箱发送重置链接重置密码
文章链接:https://cloud.tencent.com/developer/article/2465281
Lorin 洛林
2024/11/13
4440
SpringBoot集成QQ/网易/Gmail邮箱发送邮件
添加依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-mail</artifactId> </dependency> application.yml配置文件新增 spring: mail: # qq host: smtp.qq.com #发送邮件服务器 username: xx@qq.com #QQ邮箱 password:
高大北
2022/12/27
1.5K0
Spring Boot 发送邮件实战全解析
欢迎阅读 Spring Boot 2 实战系列[1] 电子邮件虽然近几年有点“退火”,但是在开发中依然有举足轻重的地位。在比较正式的场合我们依然通过电子邮件来传递信息和回执。今天我们就来学一下如何在 Spring Boot 下发送电子邮件。
码农小胖哥
2020/02/13
1.7K0
Spring Boot 发送邮件实战全解析
springboot 发送邮件
最近重写一个邮件服务来给告警业服务用,做成一个内置的应用。直接使用 spring 的 javamail 来实现。 公司使用腾讯企业邮箱。 这个例子正常使用,可以直接套上使用。
潇洒
2023/10/20
3920
定时邮件服务(发送考研词汇)
把所有的考研单词存储到数据库中,每天定时在两个时间点,上午7:30、下午6:30,将属于当天的单词发送到指定的邮箱中。一个月一遍。一年12遍,我想再笨的人也会背下来的。
ha_lydms
2023/08/09
2840
定时邮件服务(发送考研词汇)
基于SSM的 spring 发送邮件的实现
由于考虑到项目中需要,如果程序出现异常或其它问题,可以发送邮件来及时提醒后台维护人员处理。所以目前考虑使用JavaMail来实现邮件发送!如下,是我整理的一些内容,做个笔记记录下:(当然,在这功能实现之前,需要搭建好ssm的基本框架!)
AI码真香
2022/09/13
5070
基于SSM的 spring 发送邮件的实现
Spring发送邮件总结(附源码)
做项目用到自动发邮件功能,网上查阅很多没有给出特别详细的说明,需要自己去探索,做了很多弯路。
明明如月学长
2021/08/27
5820
spring boot 发送邮件
简单几步,实现在spring boot中发送邮件: 1、引入依赖: <dependency>   <groupId>org.springframework.boot</groupId>   <artifactId>spring-boot-starter-mail</artifactId>   </dependency>   2、application.yml中配置邮件相关的参数: spring:     mail:       host: smtp.exmail.qq.com       user
庞小明
2018/03/08
8150
Flask-10 博客通过发送邮件重置密码
修改Flask_Blog\flaskblog\models.py,修改User类,添加获得token令牌和验证token令牌的方法:
XXXX-user
2019/07/23
1.9K0
Flask-10 博客通过发送邮件重置密码
SpringBoot 发送邮件
# 依赖 compile group: 'org.springframework.boot', name: 'spring-boot-starter-mail' # 配置 spring: # 发邮件 mail: host: smtp.qq.com port: 587 username: 1185172056@qq.com password: 不是qq邮箱的密码,是授权码 properties: smtp: auth: true
喜欢天文的pony站长
2020/06/29
4620
SpringBoot 发送邮件
相关推荐
Springboot实战:发送邮件/重置密码业务
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验