diff --git a/CHANGELOG.md b/CHANGELOG.md
index cf03e39..e3fcb17 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,7 +1,7 @@
-## [2.3.0](https://github.com/chaos-zhu/easynode/releases) (2024-10-xx)
+## [2.3.0](https://github.com/chaos-zhu/easynode/releases) (2024-10-24)
* 重构本地数据库存储方式(性能提升一个level~)
-* 支持MFA2二次验证
+* 支持MFA2二次登录验证
* 优化了一些页面在移动端的展示
* 修复偶现刷新页面需重新登录的bug
diff --git a/server/app/controller/user.js b/server/app/controller/user.js
index 9219f51..efc8af7 100644
--- a/server/app/controller/user.js
+++ b/server/app/controller/user.js
@@ -24,7 +24,7 @@ let loginCountDown = forbidTimer
let forbidLogin = false
const login = async ({ res, request }) => {
- let { body: { loginName, ciphertext, jwtExpires }, ip: clientIp } = request
+ let { body: { loginName, ciphertext, jwtExpires, mfa2Token }, ip: clientIp } = request
if (!loginName && !ciphertext) return res.fail({ msg: '请求非法!' })
if (forbidLogin) return res.fail({ msg: `禁止登录! 倒计时[${ loginCountDown }s]后尝试登录或重启面板服务` })
loginErrCount++
@@ -55,10 +55,13 @@ const login = async ({ res, request }) => {
// 登录流程
try {
- // console.log('ciphertext', ciphertext)
let loginPwd = await RSADecryptAsync(ciphertext)
- // console.log('Decrypt解密password:', loginPwd)
- let { user, pwd } = await keyDB.findOneAsync({})
+ let { user, pwd, enableMFA2, secret } = await keyDB.findOneAsync({})
+ if (enableMFA2) {
+ const isValid = speakeasy.totp.verify({ secret, encoding: 'base32', token: mfa2Token, window: 1 })
+ console.log('MFA2 verfify:', isValid)
+ if (!isValid) return res.fail({ msg: '验证失败' })
+ }
if (loginName === user && loginPwd === 'admin' && pwd === 'admin') {
const token = await beforeLoginHandler(clientIp, jwtExpires)
return res.success({ data: { token, jwtExpires }, msg: '登录成功,请及时修改默认用户名和密码' })
@@ -68,8 +71,8 @@ const login = async ({ res, request }) => {
const token = await beforeLoginHandler(clientIp, jwtExpires)
return res.success({ data: { token, jwtExpires }, msg: '登录成功' })
} catch (error) {
- console.log('解密失败:', error)
- res.fail({ msg: '解密失败, 请查看服务端日志' })
+ console.log('登录失败:', error.message)
+ res.fail({ msg: '登录失败, 请查看服务端日志' })
}
}
@@ -87,7 +90,7 @@ const beforeLoginHandler = async (clientIp, jwtExpires) => {
const { ip, country, city } = clientIPInfo || {}
consola.info('登录成功:', new Date(), { ip, country, city })
- // 邮件登录通知
+ // 登录通知
sendNoticeAsync('login', '登录提醒', `地点:${ country + city }\nIP: ${ ip }`)
await logDB.insertAsync({ ip, country, city, date: Date.now(), type: 'login' })
@@ -140,12 +143,7 @@ const enableMFA2 = async ({ res, request }) => {
if (!token) return res.fail({ data: false, msg: '参数错误' })
try {
// const isValid = authenticator.verify({ token, secret: tempSecret })
- const isValid = speakeasy.totp.verify({
- secret: tempSecret,
- encoding: 'base32',
- token,
- window: 1
- })
+ const isValid = speakeasy.totp.verify({ secret: tempSecret, encoding: 'base32', token, window: 1 })
if (!isValid) return res.fail({ msg: '验证失败' })
const keyConfig = await keyDB.findOneAsync({})
keyConfig.enableMFA2 = true
diff --git a/server/app/utils/verify-auth.js b/server/app/utils/verify-auth.js
index 0ad1cf1..c73d16d 100644
--- a/server/app/utils/verify-auth.js
+++ b/server/app/utils/verify-auth.js
@@ -10,7 +10,7 @@ const enumLoginCode = {
ERROR_TOKEN: -2
}
-// 校验token与登录IP
+// 校验token
const verifyAuthSync = async (token, clientIp) => {
consola.info('verifyAuthSync IP:', clientIp)
try {
diff --git a/web/package.json b/web/package.json
index 39a3b55..6993074 100644
--- a/web/package.json
+++ b/web/package.json
@@ -1,6 +1,6 @@
{
"name": "web",
- "version": "2.2.8",
+ "version": "2.3.0",
"description": "easynode-web",
"private": true,
"scripts": {
diff --git a/web/src/views/login/index.vue b/web/src/views/login/index.vue
index dacff51..d98d9d9 100644
--- a/web/src/views/login/index.vue
+++ b/web/src/views/login/index.vue
@@ -44,6 +44,17 @@
+
+
+
一次性会话
@@ -70,10 +81,7 @@