From 891eb296b988f104cd798145467a282482559b27 Mon Sep 17 00:00:00 2001 From: jmal Date: Sat, 12 Jul 2025 19:02:34 +0800 Subject: [PATCH 1/2] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=E4=B8=A4?= =?UTF-8?q?=E6=AD=A5=E9=AA=8C=E8=AF=81=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 MFA 配置页面和相关组件- 在登录流程中集成两步验证逻辑 - 添加后端接口支持 MFA 配置和验证 --- src/api/setting-api.js | 20 +++ src/api/user.js | 8 + src/locales/en_US.js | 3 + src/locales/zh_CN.js | 3 + src/store/modules/user.js | 23 ++- src/views/login/index.vue | 65 +++++-- src/views/setting/cloudManager.vue | 8 +- src/views/setting/cloudSetting/ldapConfig.vue | 16 +- src/views/setting/cloudSetting/mfaConfig.vue | 165 ++++++++++++++++++ 9 files changed, 288 insertions(+), 23 deletions(-) create mode 100644 src/views/setting/cloudSetting/mfaConfig.vue diff --git a/src/api/setting-api.js b/src/api/setting-api.js index d39bfd5..c665724 100644 --- a/src/api/setting-api.js +++ b/src/api/setting-api.js @@ -1,6 +1,26 @@ import request from '@/utils/request' export default { + setupMfaConfig() { + return request({ + url: '/mfa/setup', + method: 'post' + }) + }, + enableMfaConfig(data) { + return request({ + url: '/mfa/enable', + method: 'post', + data + }) + }, + disableMfaConfig(data) { + return request({ + url: '/mfa/disable', + method: 'post', + data + }) + }, getPreviewConfig(params) { return request({ url: '/cloud/setting/preview/config', diff --git a/src/api/user.js b/src/api/user.js index a4f7b22..3a88609 100644 --- a/src/api/user.js +++ b/src/api/user.js @@ -9,6 +9,14 @@ export function login(data) { }) } +export function verifyTotp(data) { + return request({ + url: 'public/verify-totp', + method: 'post', + data + }) +} + /** * 加载ldap配置 * @returns {*} diff --git a/src/locales/en_US.js b/src/locales/en_US.js index 221c7cd..cc6e431 100644 --- a/src/locales/en_US.js +++ b/src/locales/en_US.js @@ -25,6 +25,8 @@ export default { login: { username: 'Username', password: 'Password', + verify: 'Verify', + mfaCode: 'MFA Verification Code', login: 'Login', logout: 'Logout', create: 'Create', @@ -69,6 +71,7 @@ export default { previewConfig: 'Preview Config', ocrConfig: 'OCR Config', ldapAuth: 'LDAP Auth', + mfaAuth: 'Two-Factor Authentication', startSync: 'Start scanning? ', confirmDeleteMenuAndRole: 'This operation will update and reset the system\'s default menus and roles (the menus and roles you have added yourself will not be affected). Are you sure?', resetSuccessfully: 'Reset successfully', diff --git a/src/locales/zh_CN.js b/src/locales/zh_CN.js index 7037b06..2b71ebe 100644 --- a/src/locales/zh_CN.js +++ b/src/locales/zh_CN.js @@ -25,6 +25,8 @@ export default { login: { username: '用户名', password: '密码', + verify: '验证', + mfaCode: 'MFA 验证码', login: '登录', logout: '登出', create: '创建', @@ -69,6 +71,7 @@ export default { previewConfig: '预览配置', ocrConfig: 'OCR配置', ldapAuth: 'LDAP认证', + mfaAuth: '两步验证', startSync: '是否开始扫描? ', confirmDeleteMenuAndRole: '此操作会更新重置系统默认的菜单和角色(您自己添加的菜单和角色将不受影响), 确定吗? ', resetSuccessfully: '重置成功', diff --git a/src/store/modules/user.js b/src/store/modules/user.js index 49eedb0..e19b582 100644 --- a/src/store/modules/user.js +++ b/src/store/modules/user.js @@ -1,4 +1,4 @@ -import { login, logout, getInfo } from '@/api/user' +import { login, logout, getInfo, verifyTotp } from '@/api/user' import fileApi from '@/api/file-api' import menuApi from '@/api/menu' import { setLogo } from '@/utils/logo' @@ -119,9 +119,29 @@ const actions = { const { username, password, rememberMe } = userInfo return new Promise((resolve, reject) => { login({ username: username.trim(), password: password, rememberMe: rememberMe }).then(response => { + const { data } = response + if (data.mfaRequired) { + resolve(data) + } else { + commit('SET_TOKEN', data['jmal-token']) + commit('SET_NAME', username.trim()) + commit('SET_USERID', data.userId) + resolve() + } + }).catch(error => { + reject(error) + }) + }) + }, + + verifyMfaCode({ commit }, loginForm) { + const { username, password, rememberMe, mfaCode, mfaToken } = loginForm + return new Promise((resolve, reject) => { + verifyTotp({ username: username.trim(), password: password, rememberMe: rememberMe, mfaCode: mfaCode, mfaToken: mfaToken }).then(response => { const { data } = response commit('SET_TOKEN', data['jmal-token']) commit('SET_NAME', username.trim()) + commit('SET_USERID', data.userId) resolve() }).catch(error => { @@ -129,6 +149,7 @@ const actions = { }) }) }, + // 验证提取码 validShareCode({commit}, {shareId, shareCode}) { return new Promise((resolve, reject) => { diff --git a/src/views/login/index.vue b/src/views/login/index.vue index e550d51..d538a32 100644 --- a/src/views/login/index.vue +++ b/src/views/login/index.vue @@ -21,7 +21,7 @@

{{ $t('login.createAdmin') }}

- + @@ -36,7 +36,7 @@ /> - + @@ -56,7 +56,19 @@ - + + + + + @@ -80,7 +92,8 @@ - {{initialize?$t('login.create'):$t('login.login')}} + {{initialize?$t('login.create'):$t('login.login')}} + {{$t('login.verify')}} @@ -98,9 +111,9 @@ + + From 26aafce0a3612ff8f550df5b85b50a1d4d4b6188 Mon Sep 17 00:00:00 2001 From: jmal Date: Sat, 12 Jul 2025 19:19:23 +0800 Subject: [PATCH 2/2] =?UTF-8?q?refactor(login):=20=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E7=99=BB=E5=BD=95=E9=A1=B5=E9=9D=A2=20MFA=20=E9=AA=8C=E8=AF=81?= =?UTF-8?q?=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 调整 MFA 验证码加载状态管理,提升用户体验 - 移除多余的 catch 块,简化代码结构- 在 mfaConfig 组件中: - 优化二维码显示样式,移除不必要的 fit 属性 - 修正 el-input 组件的 v-model 绑定方式 --- src/views/login/index.vue | 5 +++-- src/views/setting/cloudSetting/mfaConfig.vue | 7 +++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/views/login/index.vue b/src/views/login/index.vue index d538a32..92b9ca3 100644 --- a/src/views/login/index.vue +++ b/src/views/login/index.vue @@ -282,10 +282,11 @@ export default { this.$store.dispatch('user/verifyMfaCode', this.loginForm).then(() => { this.$store.dispatch('user/setMenuList').then(() => { this.$router.push({ path: this.redirect || '/' }) - this.verifyMfaCodeLoading = false - }).catch(() => { + }).finally(() => { this.verifyMfaCodeLoading = false }) + }).catch(() => { + this.verifyMfaCodeLoading = false }) }, validUsername() { diff --git a/src/views/setting/cloudSetting/mfaConfig.vue b/src/views/setting/cloudSetting/mfaConfig.vue index 58765e4..d609d4e 100644 --- a/src/views/setting/cloudSetting/mfaConfig.vue +++ b/src/views/setting/cloudSetting/mfaConfig.vue @@ -25,8 +25,7 @@