From 9745114c8d5e79b2415f064c5ec7b4cc600210ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E7=BF=94?= Date: Sat, 4 Apr 2026 21:36:27 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E6=B7=BB=E5=8A=A0API=E8=AF=B7=E6=B1=82?= =?UTF-8?q?=E7=AD=BE=E5=90=8D=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 问题:认证管理器缺少签名头导致登录失败 修复:添加签名生成逻辑,与前端保持一致 - 使用crypto模块生成HMAC-SHA256签名 - 添加X-Signature、X-Timestamp、X-Nonce头 - 改进错误消息显示详细错误信息 --- novalon-manage-api/Test.java | 1 + .../shared/role-auth-manager.ts | 44 ++++++++++++++++--- 2 files changed, 40 insertions(+), 5 deletions(-) create mode 100644 novalon-manage-api/Test.java diff --git a/novalon-manage-api/Test.java b/novalon-manage-api/Test.java new file mode 100644 index 0000000..6c7953e --- /dev/null +++ b/novalon-manage-api/Test.java @@ -0,0 +1 @@ +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; public class Test { public static void main(String[] args) { BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(); String hash = "$2a$12$nZ1EMUpZQljbnEdIKzH72eHlDJKUmHmHppnTTVth/SlHs5VpSAr8C"; System.out.println("Match Test@123: " + encoder.matches("Test@123", hash)); } } diff --git a/novalon-manage-web/e2e/role-based-tests/shared/role-auth-manager.ts b/novalon-manage-web/e2e/role-based-tests/shared/role-auth-manager.ts index 7f43152..34647aa 100644 --- a/novalon-manage-web/e2e/role-based-tests/shared/role-auth-manager.ts +++ b/novalon-manage-web/e2e/role-based-tests/shared/role-auth-manager.ts @@ -1,14 +1,17 @@ import { RoleFactory } from '../roles/role-factory'; +import crypto from 'crypto'; interface TokenCache { token: string; expiresAt: number; } +const SIGNATURE_SECRET = 'NovalonManageSystemSecretKey2026'; + export class RoleAuthManager { private static tokenCache: Map = new Map(); private static readonly API_BASE_URL = process.env.VITE_API_BASE_URL || 'http://localhost:8084'; - private static readonly TOKEN_EXPIRY_BUFFER = 60000; // 1分钟缓冲 + private static readonly TOKEN_EXPIRY_BUFFER = 60000; static async getRoleToken(roleName: string): Promise { const cached = this.tokenCache.get(roleName); @@ -22,23 +25,54 @@ export class RoleAuthManager { this.tokenCache.set(roleName, { token, - expiresAt: Date.now() + 3600000 // 假设token有效期1小时 + expiresAt: Date.now() + 3600000 }); return token; } + private static generateSignatureHeaders(method: string, path: string, body: string): Record { + const timestamp = Date.now(); + const nonce = `${timestamp.toString(36)}-${Math.random().toString(36).substring(2, 15)}`; + + const stringToSign = [ + method.toUpperCase(), + path, + '', + body || '', + timestamp.toString(), + nonce + ].join('\n'); + + const signature = crypto + .createHmac('sha256', SIGNATURE_SECRET) + .update(stringToSign) + .digest('base64'); + + return { + 'X-Signature': signature, + 'X-Timestamp': timestamp.toString(), + 'X-Nonce': nonce + }; + } + private static async authenticateWithBackend(credentials: { username: string; password: string }): Promise { - const response = await fetch(`${this.API_BASE_URL}/api/auth/login`, { + const path = '/api/auth/login'; + const body = JSON.stringify(credentials); + const signatureHeaders = this.generateSignatureHeaders('POST', path, body); + + const response = await fetch(`${this.API_BASE_URL}${path}`, { method: 'POST', headers: { 'Content-Type': 'application/json', + ...signatureHeaders }, - body: JSON.stringify(credentials), + body, }); if (!response.ok) { - throw new Error(`Authentication failed for user ${credentials.username}: ${response.statusText}`); + const errorText = await response.text(); + throw new Error(`Authentication failed for user ${credentials.username}: ${response.statusText} - ${errorText}`); } const data = await response.json();