From f70f5dcff8e0ac524f8c0631727f0adbad8b40ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A4=8F=E7=8A=80=E9=BA=9F?= Date: Fri, 28 Aug 2026 00:29:00 +0800 Subject: [PATCH] =?UTF-8?q?chore(other):=20=E7=99=BB=E5=BD=95=E4=BB=85?= =?UTF-8?q?=E5=8A=A0=E5=AF=86=E5=AF=86=E7=A0=81=E5=AD=97=E6=AE=B5=EF=BC=88?= =?UTF-8?q?=E8=B1=81=E5=85=8D=E6=95=B4=E4=BD=93=E5=8A=A0=E5=AF=86=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - loginApi 恒走密码字段混合加密,提交 {username, encryptedKey, encryptedData} - 请求拦截器豁免 /auth/login 整体加密 --- .workbuddy/memory/CHANGELOG.md | 2 ++ apps/web-tdesign/src/api/core/auth.ts | 17 +++++------------ apps/web-tdesign/src/api/request.ts | 6 ++++-- 3 files changed, 11 insertions(+), 14 deletions(-) diff --git a/.workbuddy/memory/CHANGELOG.md b/.workbuddy/memory/CHANGELOG.md index 9e33a4d..840a3e9 100644 --- a/.workbuddy/memory/CHANGELOG.md +++ b/.workbuddy/memory/CHANGELOG.md @@ -1,6 +1,8 @@ # admin.xpcool.com 变更记录 > 倒序:最新在上。格式:YYYY-MM-DD | 类型 | 摘要 +2026-08-27 | CHG | 登录接口豁免全量加密,永远只加密密码字段:loginApi 始终走 hybridEncryptLogin(提交 {username 明文, encryptedKey, encryptedData});request.ts PLAIN_ROUTE_FRAGMENTS 增 /auth/login(请求拦截器不再整体加密登录 body)。全量模式 5 项 + dev 15 项 E2E 验证通过 + 2026-08-27 | CHG | 全量请求/响应加密(生产 VITE_GLOB_API_ENCRYPT=true):①crypto.ts 增 encryptPayload(整体加密任意对象,返回 aesKeyB64 会话密钥)+ decryptPayload(AES-GCM 解密后端响应,密文=nonce||ciphertext||tag);②request.ts 增 installCryptoInterceptors——请求拦截器将整个 JSON body 混合加密(public-key 豁免、FormData/无 body 跳过)、响应解密拦截器最先注册(axios 响应拦截器按注册顺序执行)还原 {code,message,data},requestClient 与 baseRequestClient 均安装;apiFullBodyEncrypt/fetchPublicKeyForCrypto 导出;③auth.ts loginApi、system.ts createAdmin/resetAdminPassword 全量模式下直接传明文密码由拦截器整体加密(开发模式仍走密码字段混合加密);④.env.production VITE_GLOB_API_ENCRYPT=true / .env.development=false。⚠️TS 坑:Uint8Array.subarray 返回 ArrayBufferLike 泛型不满足 BufferSource,需 new Uint8Array() 拷贝;axios 类型从 @vben/request re-export 导入(web-tdesign 未声明 axios 依赖)。typecheck+oxlint 全绿,全量/仅密码两种模式 E2E 均通过 2026-08-27 | CHG | 登录/创建/重置密码全链路加密传输(RSA+AES 混合加密,与后端配套):①新增 src/utils/crypto.ts——Web Crypto API 零依赖:importKey('spki') 导入后端公钥、随机 AES-256-GCM 加密载荷(nonce 12B 前置、GCM tag 随密文)、RSA-OAEP(SHA-256) 加密 AES 密钥,输出 {encryptedKey, encryptedData};公钥 5 分钟内存缓存。②api/core/auth.ts:getPublicKeyApi(并发去重)+ loginApi 先拉公钥再加密 username/password 提交,网络传输无明文密码。③api/system.ts:createAdmin/resetAdminPassword 的 password 走 hybridEncryptField 加密(encryptField 复用公钥缓存)。④注意:Web Crypto 仅安全上下文可用(https/localhost/127.0.0.1),http://IP 访问会明确报错。typecheck + oxlint 全绿 diff --git a/apps/web-tdesign/src/api/core/auth.ts b/apps/web-tdesign/src/api/core/auth.ts index 0585a1e..a733ce2 100644 --- a/apps/web-tdesign/src/api/core/auth.ts +++ b/apps/web-tdesign/src/api/core/auth.ts @@ -1,5 +1,4 @@ import { - apiFullBodyEncrypt, baseRequestClient, fetchPublicKeyForCrypto, requestClient, @@ -30,18 +29,12 @@ export async function getPublicKeyApi(): Promise { } /** - * 登录(密码加密传输,对称+非对称结合): - * - 生产(全量加密):提交明文 {username,password},由请求拦截器整体加密 body; - * - 开发(仅密码加密):前端随机 AES 密钥加密 {username,password,ts},RSA 公钥加密 AES 密钥, - * 提交 {encryptedKey, encryptedData}。两种模式网络传输均无明文密码。 + * 登录(密码加密传输,对称+非对称结合)。 + * 登录永远只加密密码字段:前端随机 AES 密钥加密 {username,password,ts}, + * RSA 公钥加密 AES 密钥,提交 {username, encryptedKey, encryptedData}, + * 该接口在全量加密模式下同样豁免整体加密,便于兼容与联调。 */ export async function loginApi(data: AuthApi.LoginParams) { - if (apiFullBodyEncrypt) { - return requestClient.post( - '/api/service/admin/system/auth/login', - { username: data.username, password: data.password }, - ); - } const publicKey = await getPublicKeyApi(); const { encryptedKey, encryptedData } = await hybridEncryptLogin( publicKey, @@ -50,7 +43,7 @@ export async function loginApi(data: AuthApi.LoginParams) { ); return requestClient.post( '/api/service/admin/system/auth/login', - { encryptedKey, encryptedData }, + { username: data.username, encryptedKey, encryptedData }, ); } diff --git a/apps/web-tdesign/src/api/request.ts b/apps/web-tdesign/src/api/request.ts index 5c52d97..c6053bf 100644 --- a/apps/web-tdesign/src/api/request.ts +++ b/apps/web-tdesign/src/api/request.ts @@ -37,8 +37,10 @@ const { apiURL } = useAppConfig(import.meta.env, import.meta.env.PROD); export const apiFullBodyEncrypt = import.meta.env.VITE_GLOB_API_ENCRYPT === 'true'; -/** 全量加密豁免端点(path 片段匹配),此类请求明文收发 */ -const PLAIN_ROUTE_FRAGMENTS = ['/public-key']; +/** 全量加密豁免端点(path 片段匹配): + * - public-key:必须先明文拿到公钥才能加密; + * - 登录接口:永远只加密密码字段(username 明文 + 密码字段混合加密),不参与整体加密。 */ +const PLAIN_ROUTE_FRAGMENTS = ['/public-key', '/auth/login']; // 公钥拉取并发去重:同一时刻只发一次请求。 let publicKeyPromise: null | Promise = null;