关注我的微信公众号:后端技术漫谈
不定期推送关于后端开发、爬虫、算法题、数据结构方面的原创技术文章,以及生活中的逸闻趣事。
我目前是一名后端开发工程师。主要关注后端开发,数据安全,网络爬虫,物联网,边缘计算等方向。
原创博客主要内容
前言
忘记密码并通过邮件重置密码是一个常见的业务需求,在开发我的个人小项目过程中,也需要用到这个业务,今天就给大家带来一个业务实战。
根据controller中函数分为两个部分:
org.springframework.boot spring-boot-starter-mail
其中reset_token由UUID生成,type默认为resetPassword(方便以后新增需求),user_id为用户表用户id
-- ---------------------------- -- 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。
# 发送邮件配置 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
@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 ResponseDatasendValidationEmail(@ApiParam("邮箱地址") @RequestParam("email") String email, HttpServletRequest request){ ResponseData responseData = new ResponseData<>(); List 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("您正在申请重置密码,请点击此链接重置密码: " + 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 resetPassword(@ApiParam("token") @RequestParam("token") String token, @ApiParam("密码") @RequestParam("password") String password, @ApiParam("密码确认") @RequestParam("confirmPassword") String confirmPassword){ ResponseData responseData = new ResponseData<>(); // 通过token找到validate记录 List 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; } }
public interface ValidateService { void sendPasswordResetEmail(SimpleMailMessage email); int insertNewResetRecord(Validate validate, User users, String token); ListfindUserByResetToken(String resetToken); boolean validateLimitation(String email, long requestPerDay, long interval, String token); boolean sendValidateLimitation(String email, long requestPerDay, long interval); }
@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 ListfindUserByResetToken(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 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 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
原创博客主要内容
同步更新以下博客
1. Csdn
http://blog.csdn.net/qqxx6661
拥有专栏:Leetcode题解(Java/Python)、Python爬虫开发、面试助攻手册
2. 知乎
https://www.zhihu.com/people/yang-zhen-dong-1/
拥有专栏:码农面试助攻手册
3. 掘金
https://juejin.im/user/5b48015ce51d45191462ba55
4. 简书
https://www.jianshu.com/u/b5f225ca2376
个人公众号:后端数据漫谈
页面更新:2024-03-08
本站资料均由网友自行发布提供,仅用于学习交流。如有版权问题,请与我联系,QQ:4156828
© CopyRight 2020-2024 All Rights Reserved. Powered By 71396.com 闽ICP备11008920号-4
闽公网安备35020302034903号