-
-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
718 additions
and
29 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,271 @@ | ||
/* controller/forgetpw.js */ | ||
|
||
const CryptoJS = require('crypto-js'); | ||
const config = require('../config'); | ||
const User = require('../service/user'); | ||
const Forgetpw = require('../service/forgetpw'); | ||
|
||
module.exports = { | ||
frontend: async (ctx) => { | ||
const userData = await User.getUserInfo(ctx).then((ret) => ret); | ||
await ctx.render('forgetpw', { | ||
config: config.common, | ||
user: userData, | ||
}); | ||
}, | ||
handle: async (ctx) => { | ||
const data = {}; | ||
const time = Date.now(); | ||
let body = {}; | ||
const userData = await User.getUserInfo(ctx).then((ret) => ret); | ||
if (userData.isLoggedIn) { | ||
data.code = -1; | ||
data.msg = '你已登录'; | ||
ctx.set('Content-Type', 'application/json'); | ||
ctx.body = JSON.stringify(data); | ||
return; | ||
} | ||
if (!ctx.request.body.data) { | ||
data.code = -1; | ||
data.msg = '解析数据发生错误'; | ||
ctx.set('Content-Type', 'application/json'); | ||
ctx.body = JSON.stringify(data); | ||
return; | ||
} | ||
if (!ctx.session.key || !ctx.session.key.secret || !ctx.session.key.iv || !ctx.session.key.ts || time - ctx.session.key.ts > 300000) { | ||
data.code = -1; | ||
data.msg = '传输凭证无效'; | ||
ctx.set('Content-Type', 'application/json'); | ||
ctx.body = JSON.stringify(data); | ||
return; | ||
} | ||
body = ctx.request.body.data; | ||
const secret = CryptoJS.enc.Hex.parse(ctx.session.key.secret); | ||
const iv = CryptoJS.enc.Hex.parse(ctx.session.key.iv); | ||
try { | ||
body = JSON.parse(CryptoJS.AES.decrypt(body, secret, { iv, padding: CryptoJS.pad.ZeroPadding }).toString(CryptoJS.enc.Utf8)); | ||
} catch (error) { | ||
data.code = -1; | ||
data.msg = '解密数据发生错误'; | ||
ctx.set('Content-Type', 'application/json'); | ||
ctx.body = JSON.stringify(data); | ||
return; | ||
} | ||
|
||
if (!ctx.session.captcha || !ctx.session.captcha.text || !ctx.session.captcha.ts || time - ctx.session.captcha.ts > 300000) { | ||
ctx.session.captcha.text = Math.random(); | ||
data.code = -1; | ||
data.msg = '验证码超时/无效'; | ||
ctx.set('Content-Type', 'application/json'); | ||
ctx.body = JSON.stringify(data); | ||
return; | ||
} | ||
|
||
if (body.captcha !== ctx.session.captcha.text) { | ||
ctx.session.captcha.text = Math.random(); | ||
data.code = -1; | ||
data.msg = '验证码无效'; | ||
ctx.set('Content-Type', 'application/json'); | ||
ctx.body = JSON.stringify(data); | ||
return; | ||
} | ||
if (!body.email) { | ||
data.code = -1; | ||
data.msg = '邮箱不能为空'; | ||
ctx.set('Content-Type', 'application/json'); | ||
ctx.body = JSON.stringify(data); | ||
return; | ||
} | ||
|
||
if (!/^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/.test(body.email)) { | ||
data.code = -1; | ||
data.msg = '邮箱格式不正确'; | ||
ctx.set('Content-Type', 'application/json'); | ||
ctx.body = JSON.stringify(data); | ||
return; | ||
} | ||
|
||
try { | ||
const emailExists = await User.isUserExists('email', body.email).then((exists) => exists); | ||
if (!emailExists) { | ||
data.code = -1; | ||
data.msg = '该邮箱还未注册'; | ||
ctx.set('Content-Type', 'application/json'); | ||
ctx.body = JSON.stringify(data); | ||
return; | ||
} | ||
|
||
const userInfo = await User.searchUserInfoByEmail(body.email).then((ret) => ret); | ||
|
||
const isVerifyTokenExists = await Forgetpw.isVerifyTokenExists(userInfo.id).then((ret) => ret); | ||
|
||
// 密码重置token还未过期 | ||
if (isVerifyTokenExists) { | ||
const verifyTokenTime = await Forgetpw.getVerifyTokenTime(userInfo.id).then((ret) => ret); | ||
if (!verifyTokenTime) { | ||
data.code = -1; | ||
data.msg = '未知错误,请稍后重试'; | ||
ctx.set('Content-Type', 'application/json'); | ||
ctx.body = JSON.stringify(data); | ||
return; | ||
} | ||
const timeNow = Math.floor(new Date().getTime() / 1000); | ||
data.code = -1; | ||
data.msg = `约${Math.ceil((verifyTokenTime - timeNow) / 60)}分钟后才能再次发送密码重置邮件`; | ||
ctx.set('Content-Type', 'application/json'); | ||
ctx.body = JSON.stringify(data); | ||
return; | ||
} | ||
|
||
const token = Forgetpw.genVerifyToken(); | ||
await Forgetpw.storeVerifyTokenToRedis(userInfo.id, token).then((ret) => ret); | ||
|
||
const result = await Forgetpw.sendVerifyUrl(userInfo.email, userInfo.playername, userInfo.id, token).then((ret) => ret); | ||
|
||
if (!result) { | ||
data.code = -1; | ||
data.msg = '未知错误'; | ||
ctx.set('Content-Type', 'application/json'); | ||
ctx.body = JSON.stringify(data); | ||
return; | ||
} | ||
|
||
data.code = 1000; | ||
data.msg = '邮件已发送,请查看您的收件箱'; | ||
ctx.set('Content-Type', 'application/json'); | ||
ctx.body = JSON.stringify(data); | ||
|
||
ctx.session.captcha.text = Math.random(); | ||
} catch (error) { | ||
data.code = -1; | ||
data.msg = '未知错误'; | ||
ctx.set('Content-Type', 'application/json'); | ||
ctx.body = JSON.stringify(data); | ||
} | ||
}, | ||
changeFrontend: async (ctx) => { | ||
const userData = await User.getUserInfo(ctx).then((ret) => ret); | ||
const playerId = ctx.params.id; | ||
const { token } = ctx.params; | ||
const result = await Forgetpw.isVerifyTokenCurrect(playerId, token).then((ret) => ret); | ||
let isCorrect = false; | ||
if (result) { | ||
isCorrect = true; | ||
} | ||
|
||
await ctx.render('forgetpw_change', { | ||
config: config.common, | ||
user: userData, | ||
isCorrect, | ||
}); | ||
}, | ||
changeHandle: async (ctx) => { | ||
const data = {}; | ||
const time = Date.now(); | ||
let body = {}; | ||
const userData = await User.getUserInfo(ctx).then((ret) => ret); | ||
if (userData.isLoggedIn) { | ||
data.code = -1; | ||
data.msg = '你已登录'; | ||
ctx.set('Content-Type', 'application/json'); | ||
ctx.body = JSON.stringify(data); | ||
return; | ||
} | ||
if (!ctx.request.body.data) { | ||
data.code = -1; | ||
data.msg = '解析数据发生错误'; | ||
ctx.set('Content-Type', 'application/json'); | ||
ctx.body = JSON.stringify(data); | ||
return; | ||
} | ||
if (!ctx.session.key || !ctx.session.key.secret || !ctx.session.key.iv || !ctx.session.key.ts || time - ctx.session.key.ts > 300000) { | ||
data.code = -1; | ||
data.msg = '传输凭证无效'; | ||
ctx.set('Content-Type', 'application/json'); | ||
ctx.body = JSON.stringify(data); | ||
return; | ||
} | ||
body = ctx.request.body.data; | ||
const secret = CryptoJS.enc.Hex.parse(ctx.session.key.secret); | ||
const iv = CryptoJS.enc.Hex.parse(ctx.session.key.iv); | ||
try { | ||
body = JSON.parse(CryptoJS.AES.decrypt(body, secret, { iv, padding: CryptoJS.pad.ZeroPadding }).toString(CryptoJS.enc.Utf8)); | ||
} catch (error) { | ||
data.code = -1; | ||
data.msg = '解密数据发生错误'; | ||
ctx.set('Content-Type', 'application/json'); | ||
ctx.body = JSON.stringify(data); | ||
return; | ||
} | ||
|
||
if (!ctx.session.captcha || !ctx.session.captcha.text || !ctx.session.captcha.ts || time - ctx.session.captcha.ts > 300000) { | ||
ctx.session.captcha.text = Math.random(); | ||
data.code = -1; | ||
data.msg = '验证码超时/无效'; | ||
ctx.set('Content-Type', 'application/json'); | ||
ctx.body = JSON.stringify(data); | ||
return; | ||
} | ||
|
||
if (body.captcha !== ctx.session.captcha.text) { | ||
ctx.session.captcha.text = Math.random(); | ||
data.code = -1; | ||
data.msg = '验证码无效'; | ||
ctx.set('Content-Type', 'application/json'); | ||
ctx.body = JSON.stringify(data); | ||
return; | ||
} | ||
if (!body.password) { | ||
data.code = -1; | ||
data.msg = '密码不能为空'; | ||
ctx.set('Content-Type', 'application/json'); | ||
ctx.body = JSON.stringify(data); | ||
return; | ||
} | ||
|
||
if (body.password.length > 150) { | ||
data.code = -1; | ||
data.msg = '密码不合法'; | ||
ctx.set('Content-Type', 'application/json'); | ||
ctx.body = JSON.stringify(data); | ||
return; | ||
} | ||
|
||
const playerId = ctx.params.id; | ||
const { token } = ctx.params; | ||
const result = await Forgetpw.isVerifyTokenCurrect(playerId, token).then((ret) => ret); | ||
if (!result) { | ||
data.code = -1; | ||
data.msg = '链接无效或已过期'; | ||
ctx.set('Content-Type', 'application/json'); | ||
ctx.body = JSON.stringify(data); | ||
return; | ||
} | ||
|
||
await Forgetpw.delVerifyToken(playerId); | ||
|
||
try { | ||
// 获取用户信息 | ||
const userInfo = await User.searchUserInfoByID(playerId).then((ret) => ret); | ||
|
||
const changepwResult = await User.changeUserPassword(userInfo.email, body.password).then((ret) => ret); | ||
if (!changepwResult) { | ||
data.code = -1; | ||
data.msg = '修改密码时发生错误'; | ||
ctx.set('Content-Type', 'application/json'); | ||
ctx.body = JSON.stringify(data); | ||
return; | ||
} | ||
ctx.session.captcha.text = Math.random(); | ||
data.code = 1000; | ||
data.msg = '密码修改成功'; | ||
ctx.set('Content-Type', 'application/json'); | ||
ctx.body = JSON.stringify(data); | ||
} catch (error) { | ||
data.code = -1; | ||
data.msg = '未知错误'; | ||
ctx.set('Content-Type', 'application/json'); | ||
ctx.body = JSON.stringify(data); | ||
} | ||
}, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
(() => { | ||
function refreshCaptcha() { | ||
document.querySelector('#img-captcha').src = `/api/captcha?t=${Date.now()}`; | ||
} | ||
|
||
function postData(data) { | ||
fetch('/forgetpw', { | ||
method: 'POST', | ||
headers: { | ||
'Content-Type': 'application/x-www-form-urlencoded', | ||
}, | ||
body: `data=${encodeURIComponent(data)}`, | ||
}) | ||
.then((result) => result.json()) | ||
.then((json) => { | ||
switch (json.code) { | ||
case -1: | ||
notyf.open({ | ||
type: 'error', | ||
message: json.msg, | ||
}); | ||
break; | ||
case 1000: | ||
notyf.open({ | ||
type: 'success', | ||
message: '邮件已发送,有效期5分钟,请查收', | ||
}); | ||
break; | ||
default: | ||
throw new Error('未知错误'); | ||
} | ||
refreshCaptcha(); | ||
document.querySelector('.btn-forgetpw').removeAttribute('disabled'); | ||
}) | ||
.catch((e) => { | ||
notyf.open({ | ||
type: 'error', | ||
message: e, | ||
}); | ||
refreshCaptcha(); | ||
document.querySelector('.btn-forgetpw').removeAttribute('disabled'); | ||
}); | ||
} | ||
|
||
document.querySelector('#img-captcha').addEventListener('click', refreshCaptcha); | ||
|
||
document.querySelector('.btn-forgetpw').addEventListener('click', () => { | ||
const email = document.querySelector('#inputEmail').value; | ||
const captcha = document.querySelector('#inputCaptcha').value; | ||
if (!email || !captcha) { | ||
notyf.open({ | ||
type: 'error', | ||
message: '<b>邮箱</b>/<b>验证码</b>不能为空', | ||
}); | ||
return; | ||
} | ||
|
||
if (!/^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/.test(email)) { | ||
notyf.open({ | ||
type: 'error', | ||
message: '输入的邮箱格式不正确', | ||
}); | ||
return; | ||
} | ||
|
||
document.querySelector('.btn-forgetpw').setAttribute('disabled', 'true'); | ||
notyf.open({ | ||
type: 'info', | ||
message: '提交中,请稍后...', | ||
}); | ||
|
||
fetch('/api/genkey', { method: 'POST' }) | ||
.then((result) => result.text()) | ||
.then((text) => { | ||
if (text.length === 86) { | ||
let secret = ''; | ||
let iv = ''; | ||
for (let i = 0; i < text.length; i += 1) { | ||
if (i % 2 === 0) { | ||
secret += text[i]; | ||
} else { | ||
iv += text[i]; | ||
} | ||
} | ||
|
||
secret = CryptoJS.enc.Hex.parse(atob(`${secret}=`)); | ||
iv = CryptoJS.enc.Hex.parse(atob(`${iv}=`)); | ||
const data = { | ||
email, | ||
captcha, | ||
}; | ||
const encrypted = CryptoJS.AES.encrypt(JSON.stringify(data), secret, { iv, padding: CryptoJS.pad.ZeroPadding }); | ||
postData(encrypted.ciphertext.toString(CryptoJS.enc.Base64)); | ||
} else { | ||
throw new Error('传输凭证获取失败'); | ||
} | ||
}) | ||
.catch((e) => { | ||
notyf.open({ | ||
type: 'error', | ||
message: e, | ||
}); | ||
document.querySelector('.btn-forgetpw').removeAttribute('disabled'); | ||
}); | ||
}); | ||
})(); |
Oops, something went wrong.