From fda050d3fec5ff41dab176b7e452fee91d571631 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=89=8B=E7=93=9C=E4=B8=80=E5=8D=81=E9=9B=AA?= Date: Sat, 19 Apr 2025 19:50:52 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=8A=A0=E5=BC=BA=E5=AE=89=E5=85=A8?= =?UTF-8?q?=E6=80=A7=20=E4=BC=A0=E8=BE=93=E8=BF=87=E7=A8=8B=E4=BD=BF?= =?UTF-8?q?=E7=94=A8salt=20sha256?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- napcat.webui/package.json | 2 ++ napcat.webui/src/controllers/webui_manager.ts | 5 ++-- src/webui/src/api/Auth.ts | 13 +++++---- src/webui/src/helper/SignToken.ts | 28 +++++++++++++++---- src/webui/src/middleware/auth.ts | 7 +++-- src/webui/src/types/sign_token.d.ts | 2 +- 6 files changed, 40 insertions(+), 17 deletions(-) diff --git a/napcat.webui/package.json b/napcat.webui/package.json index bd3c4239..abe025ed 100644 --- a/napcat.webui/package.json +++ b/napcat.webui/package.json @@ -55,6 +55,7 @@ "ahooks": "^3.8.4", "axios": "^1.7.9", "clsx": "^2.1.1", + "crypto-js": "^4.2.0", "echarts": "^5.5.1", "event-source-polyfill": "^1.0.31", "framer-motion": "^12.0.6", @@ -88,6 +89,7 @@ "@eslint/js": "^9.19.0", "@react-types/shared": "^3.26.0", "@trivago/prettier-plugin-sort-imports": "^5.2.2", + "@types/crypto-js": "^4.2.2", "@types/event-source-polyfill": "^1.0.5", "@types/fabric": "^5.3.9", "@types/node": "^22.12.0", diff --git a/napcat.webui/src/controllers/webui_manager.ts b/napcat.webui/src/controllers/webui_manager.ts index ac472126..dfd3e741 100644 --- a/napcat.webui/src/controllers/webui_manager.ts +++ b/napcat.webui/src/controllers/webui_manager.ts @@ -3,7 +3,7 @@ import { EventSourcePolyfill } from 'event-source-polyfill' import { LogLevel } from '@/const/enum' import { serverRequest } from '@/utils/request' - +import CryptoJS from "crypto-js"; export interface Log { level: LogLevel message: string @@ -17,9 +17,10 @@ export default class WebUIManager { } public static async loginWithToken(token: string) { + const sha256 = CryptoJS.SHA256(token + '.napcat').toString(); const { data } = await serverRequest.post>( '/auth/login', - { token } + { hash: sha256 } ) return data.data.Credential } diff --git a/src/webui/src/api/Auth.ts b/src/webui/src/api/Auth.ts index 53e06315..5fb8c1f3 100644 --- a/src/webui/src/api/Auth.ts +++ b/src/webui/src/api/Auth.ts @@ -20,25 +20,26 @@ export const CheckDefaultTokenHandler: RequestHandler = async (_, res) => { export const LoginHandler: RequestHandler = async (req, res) => { // 获取WebUI配置 const WebUiConfigData = await WebUiConfig.GetWebUIConfig(); - // 获取请求体中的token - const { token } = req.body; + // 获取请求体中的hash + const { hash } = req.body; // 获取客户端IP const clientIP = req.ip || req.socket.remoteAddress || ''; // 如果token为空,返回错误信息 - if (isEmpty(token)) { + if (isEmpty(hash)) { return sendError(res, 'token is empty'); } // 检查登录频率 if (!WebUiDataRuntime.checkLoginRate(clientIP, WebUiConfigData.loginRate)) { return sendError(res, 'login rate limit'); } - //验证config.token是否等于token - if (WebUiConfigData.token !== token) { + //验证config.token hash是否等于token hash + if (!AuthHelper.comparePasswordHash(WebUiConfigData.token, hash)) { return sendError(res, 'token is invalid'); } + // 签发凭证 - const signCredential = Buffer.from(JSON.stringify(AuthHelper.signCredential(WebUiConfigData.token))).toString( + const signCredential = Buffer.from(JSON.stringify(AuthHelper.signCredential(hash))).toString( 'base64' ); // 返回成功信息 diff --git a/src/webui/src/helper/SignToken.ts b/src/webui/src/helper/SignToken.ts index 50865b19..495bad56 100644 --- a/src/webui/src/helper/SignToken.ts +++ b/src/webui/src/helper/SignToken.ts @@ -5,13 +5,13 @@ export class AuthHelper { /** * 签名凭证方法。 - * @param token 待签名的凭证字符串。 + * @param hash 待签名的凭证字符串。 * @returns 签名后的凭证对象。 */ - public static signCredential(token: string): WebUiCredentialJson { + public static signCredential(hash: string): WebUiCredentialJson { const innerJson: WebUiCredentialInnerJson = { CreatedTime: Date.now(), - TokenEncoded: token, + HashEncoded: hash, }; const jsonString = JSON.stringify(innerJson); const hmac = crypto.createHmac('sha256', AuthHelper.secretKey).update(jsonString, 'utf8').digest('hex'); @@ -57,8 +57,7 @@ export class AuthHelper { const currentTime = Date.now() / 1000; const createdTime = credentialJson.Data.CreatedTime; const timeDifference = currentTime - createdTime; - - return timeDifference <= 3600 && credentialJson.Data.TokenEncoded === token; + return timeDifference <= 3600 && credentialJson.Data.HashEncoded === AuthHelper.generatePasswordHash(token); } /** @@ -85,4 +84,23 @@ export class AuthHelper { return store.exists(`revoked:${hmac}`) > 0; } + + /** + * 生成密码Hash + * @param password 密码 + * @returns 生成的Hash值 + */ + public static generatePasswordHash(password: string): string { + return crypto.createHash('sha256').update(password + '.napcat').digest().toString('hex') + } + + /** + * 对比密码和Hash值 + * @param password 密码 + * @param hash Hash值 + * @returns 布尔值,表示密码是否匹配Hash值 + */ + public static comparePasswordHash(password: string, hash: string): boolean { + return this.generatePasswordHash(password) === hash; + } } diff --git a/src/webui/src/middleware/auth.ts b/src/webui/src/middleware/auth.ts index 67d73ecd..8e2d756c 100644 --- a/src/webui/src/middleware/auth.ts +++ b/src/webui/src/middleware/auth.ts @@ -21,17 +21,18 @@ export async function auth(req: Request, res: Response, next: NextFunction) { return sendError(res, 'Unauthorized'); } // 获取token - const token = authorization[1]; + const hash = authorization[1]; + if(!hash) return sendError(res, 'Unauthorized'); // 解析token let Credential: WebUiCredentialJson; try { - Credential = JSON.parse(Buffer.from(token, 'base64').toString('utf-8')); + Credential = JSON.parse(Buffer.from(hash, 'base64').toString('utf-8')); } catch (e) { return sendError(res, 'Unauthorized'); } // 获取配置 const config = await WebUiConfig.GetWebUIConfig(); - // 验证凭证在1小时内有效且token与原始token相同 + // 验证凭证在1小时内有效 const credentialJson = AuthHelper.validateCredentialWithinOneHour(config.token, Credential); if (credentialJson) { // 通过验证 diff --git a/src/webui/src/types/sign_token.d.ts b/src/webui/src/types/sign_token.d.ts index 5bd79b69..1b6514d1 100644 --- a/src/webui/src/types/sign_token.d.ts +++ b/src/webui/src/types/sign_token.d.ts @@ -1,6 +1,6 @@ interface WebUiCredentialInnerJson { CreatedTime: number; - TokenEncoded: string; + HashEncoded: string; } interface WebUiCredentialJson {