找回密码是怎么实现的(找回密码功能的实现)
找回密码是怎么实现的(找回密码功能的实现)reset () { var _this = this // name发送到后端,正确会返回其邮箱名称(后端进行了脱敏),否则提示name工号不存在 this.$axios.post('reset' {'name': this.name}) .then(function (res) { if (res.data.success) { // 已发送重置密码链接至xx邮箱,如果邮箱不能接收,请联系管理员! var msg = '重置链接已发送至' res.data.data '邮箱,如果邮箱不能接收,请联系管理员!' _this.$notify({ t
首先表明我这个项目是个小项目,不存在高并发,不存在微服务,使用人数是个位数的....(仅用来配合目前工作使用的(非IT行业,未入行))
技术栈:- 后端:springboot项目;
- 前端:vue、elementUI。
由于项目比较小,也就不上redis了,直接使用mysql数据库,新建一个reset表 (字段:username、uuid)。
前端流程如下:1.在登陆页中增加“找回密码”按钮:
2.点击后弹出:
3.输入“工号”后点确认:
a.工号不存在:然后整个流程结束
b.工号存在:由服务端进行相关操作后,返回数据(用户邮箱且进行脱敏处理)拼接显示。
前端的js代码如下:
reset () {
var _this = this
// name发送到后端,正确会返回其邮箱名称(后端进行了脱敏),否则提示name工号不存在
this.$axios.post('reset' {'name': this.name})
.then(function (res) {
if (res.data.success) {
// 已发送重置密码链接至xx邮箱,如果邮箱不能接收,请联系管理员!
var msg = '重置链接已发送至' res.data.data '邮箱,如果邮箱不能接收,请联系管理员!'
_this.$notify({
title: '密码重置'
message: msg
type: 'success'
duration: 2000
})
_this.dialogVisible = false
} else {
//工号不存在时提示
_this.$notify.error({
title: '密码重置'
message: '请检查工号输入是否正确'
duration: 2000
})
}
})
}
后端发送邮件如下:
主要是reset这个api接口
uuid生成、邮箱名脱敏使用的是hutool工具。数据库框架使用的是mybatis-plus
@ResponseBody
public Result<String> reset(@RequestBody Map<String String> map) {
String username = map.get("name");
User user = userDao.selectOne(Wrappers.<User>lambdaQuery().eq(User::getUsername username));
if (user != null) {
String email = user.getEmail();
// 模拟化处理:邮箱名称脱敏
String emailto = DesensitizedUtil.email(email);
// reset表中存储当前id和uuid随机字符串值
// 先查询表中是否有当前username是否存在,如果有则直接创建邮件,无则新建记录并创建邮件;
Reset reset = resetMapper.selectOne(Wrappers.<Reset>lambdaQuery().eq(Reset::getUsername username));
if (reset == null) {
resetMapper.insert(new Reset(username IdUtil.simpleUUID()));
reset = resetMapper.selectOne(Wrappers.<Reset>lambdaQuery().eq(Reset::getUsername username));
}
String uuid = reset.getUuid();
//发送邮件
MimeMessage mailMessage = null;
try {
mailMessage = javaMailSender.createMimeMessage();
//是否发送的邮件是富文本(附件,图片,html等)
MimeMessageHelper helper = new MimeMessageHelper(mailMessage true);
//发送者和接收者
helper.setFrom(from);
helper.setTo(email);
//邮件标题
helper.setSubject("yderp密码重置-工号:" username);
//邮件内容拼接
helper.setText("<p>用户" username "你好:你正在进行密码重置,本链接仅当天一次有效,请点击以下按钮进行修改:</p>\n"
"\t\t<a href=\"" myAddress "resetPassword?uuid=" uuid "\"" ">点击修改密码</a>" true);
javaMailSender.send(mailMessage);
}catch (Exception e) {
return Result.failure(505 e.toString());
}
return Result.defaultSuccess(emailto);
} else {
return Result.failure(506 "当前工号不存在");
}
}
重置密码链接页面:
需要对uuid进行判断,否则跳转到无权限页
@GetMapping("/resetPassword")
public String resetPassword(String uuid){
if (uuid == null || "".equals(uuid)) {
return "403";
}
// 查询uuid是否有效
Reset one = resetMapper.selectOne(Wrappers.<Reset>lambdaQuery().eq(Reset::getUuid uuid));
if (one == null) {
return "403";
}
return "resetPassword";
}
重置密码使用的是后端模板页面:resetPassword.html
重点是:1.获取url中uuid参数的值、
2.对新密码进行正则校验、
3.修改密码请求需要将当前uuid值传递至后端进行校验。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>重置密码</title>
<style>
table {
border-collapse: collapse;
}
[v-cloak]{
display: none !important;
}
</style>
<script src="https://cdn.bootcdn.net/ajax/libs/moment.js/2.9.0/moment.min.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.9/vue.min.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/axios/0.20.0/axios.min.js"></script>
<!-- 引入组件库 -->
<script src="https://unpkg.com/element-ui/lib/index.js"></script>
<!-- 引入样式 -->
<link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
<link rel="shortcut icon" type="image/x-icon" href="/static/favicon.ico">
<script>
window.onload = function () {
new Vue({
el:"#main"
data: {
uuid: ""
password: ''
}
created() {
this.uuid = this.GetQueryString("uuid")
}
methods:{
resetPassword() {
var _this = this
var match = /^[a-zA-Z]\w{7 11}$/
if (!match.test(this.password)) {
_this.$message.error('新密码以字母开头 8-12位长度')
} else {
axios.post('upPassword' {password: _this.password uuid: _this.uuid}).then(function (res) {
if(res.data.success === true) {
_this.$message({
message: res.data.data
type: 'success'
})
}else {
_this.$message.error(res.data.message)
}
})
}
}
// 查找url中参数的方法
GetQueryString(name) {
var reg = new RegExp("(^|&)" name "=([^&]*)(&|$)");
var r = window.location.search.substr(1).match(reg);
if (r != null) {
return unescape(r[2]);
}
return null;
}
}
})
}
</script>
</head>
<body>
<div id="main">
<div style="width: 200px;margin-top:10%;margin-left: 40%;">
<el-input v-model="password" placeholder="请输入新密码"></el-input>
<div style="margin: 20px auto">
<el-button type="primary" @click="resetPassword">修改密码</el-button>
</div>
</div>
</div>
</body>
</html>
重置密码的后端api
1.需要对前端传过来的密码再次进行正则校验;
2.需要对uuid在reset表中进行查询,如果没有数据表明链接已失效;
3.修改密码中需要对密码进行加密存储;
4.修改成功后,删除reset表中相应的记录。
//修改密码
@PostMapping("/upPassword")
@ResponseBody
public Result<String> upPassword(@RequestBody Map<String String> map) {
String uuid = map.get("uuid");
String password = map.get("password");
String match = "[a-zA-Z]\\w{7 11}";
// 密码正则验证
if (uuid == null || password == null || !password.matches(match)) {
return Result.failure(501 "参数错误或密码不匹配!");
}
// 查出uuid记录
Reset reset = resetMapper.selectOne(Wrappers.<Reset>lambdaQuery().eq(Reset::getUuid uuid));
if (reset == null) {
return Result.failure(502 "当前链接已失效!");
}
String username = reset.getUsername();
User user = userDao.selectOne(Wrappers.<User>lambdaQuery().eq(User::getUsername username));
// 然后修改密码,并且删除Reset表中相应username记录,需要加密
user.setPassword(BCrypt.hashpw((password)));
int i = userDao.updateById(user);
if (i > 0) {
resetMapper.delete(Wrappers.<Reset>lambdaQuery().eq(Reset::getUsername user.getUsername()));
return Result.defaultSuccess("密码修改成功!");
}
return Result.failure(503 "其他错误!");
}
至此,密码找回功能已经实现。当然上面的api接口均要被权限控制框架所放行。
但是还有一个问题,我们在邮箱中设置显示的是链接当天一次有效,如果当天没有打开,或者当天没有修改,在第二天依然会有效,需要的是在凌晨0点进行清除reset表的操作,需要的是一个定时任务。新建一个定时配置类:
@Configuration //1.主标记配置类
@EnableScheduling // 2.开启定时任务
public class ScheduleTask {
private ResetMapper resetMapper;
public ScheduleTask(ResetMapper resetMapper) {
this.resetMapper = resetMapper;
}
//3.添加定时任务 每天0点执行清空表
@Scheduled(cron = "0 0 0 * * ?")
private void clearReset() {
//清除reset表中数据
resetMapper.clearAll();
}
}
@Mapper
@Component
public interface ResetMapper extends BaseMapper<Reset> {
@Update("truncate table reset")
void clearAll();
}
纯自学野路子来的,不要喷我,可能逻辑上或者写的格式不好看,关键是能跑得起来。