import type { InternalAxiosRequestConfig } from 'axios'; import { JSEncrypt } from 'jsencrypt'; import { SYSTEM_SERVICE_PREFIX } from '@/constants/service'; const API_ENCRYPT_HEADER = 'X-Api-Encrypt'; const API_ENCRYPT_HEADER_VALUE = 'true'; const API_ENCRYPT_CONFIG_ERROR_MSG = '前端加密配置异常,请联系管理员'; const API_ENCRYPT_REQUEST_ERROR_MSG = '请求加密失败,请刷新页面后重试'; const API_ENCRYPT_PUBLIC_KEY = `-----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv2kobVo5aSDWvmWZIKeL G7dowOLTwjdWIwy7+UmTBw3e6vJW5BNEjrnW7kiqeT97VQj2V6MMmaJufpeegACG AmhuTnG83kVLQeiXL5rlPUmdNPM8O89gSM3iMzLSUhn+rvAaHFXjKNu2xssodYn1 F26SlVO1ewwS82AAwEPSaotL7Kq8Qxg7vmZty6RcEjp7/OaYAtHfva3uewiGMp11 ZkywKPleQ3nT7HHjQgAckbNZFMhTMMqDzW5oI3KSm3sA+pWsUfRrZxUf2ws358/F KewDbbhwj3u731NbXlO+WUfv3FvbdhktXtU/15FC0b+Tx+YHIUhkNTRzuIpiG7+X cwIDAQAB -----END PUBLIC KEY-----`; const API_ENCRYPT_RULE_MAP = { [`POST ${SYSTEM_SERVICE_PREFIX}/auth/login`]: ['password'], [`POST ${SYSTEM_SERVICE_PREFIX}/auth/register`]: ['password'], [`PUT ${SYSTEM_SERVICE_PREFIX}/user/profile/update-password`]: ['oldPassword', 'newPassword'], [`POST ${SYSTEM_SERVICE_PREFIX}/user/create`]: ['password'], [`PUT ${SYSTEM_SERVICE_PREFIX}/user/update-password`]: ['password'] } as const satisfies Record; type ApiEncryptRuleKey = keyof typeof API_ENCRYPT_RULE_MAP; interface ApiEncryptRequestConfig extends InternalAxiosRequestConfig { apiEncryptProcessed?: boolean; } let encryptor: JSEncrypt | null = null; function isPlainObject(value: unknown): value is Record { return Object.prototype.toString.call(value) === '[object Object]'; } function normalizeRequestPath(url: string) { try { return new URL(url, 'http://localhost').pathname; } catch { return url.split('?')[0] || ''; } } function getApiEncryptRule(config: InternalAxiosRequestConfig) { const method = config.method?.toUpperCase(); const url = config.url; if (!method || !url) { return null; } const ruleKey = `${method} ${normalizeRequestPath(url)}` as ApiEncryptRuleKey; return API_ENCRYPT_RULE_MAP[ruleKey] ?? null; } function getEncryptor() { if (encryptor) { return encryptor; } const publicKey = API_ENCRYPT_PUBLIC_KEY.trim(); if (!publicKey.includes('BEGIN PUBLIC KEY') || !publicKey.includes('END PUBLIC KEY')) { throw new Error(API_ENCRYPT_CONFIG_ERROR_MSG); } encryptor = new JSEncrypt(); encryptor.setPublicKey(publicKey); return encryptor; } function encryptFieldValue(value: unknown) { if (typeof value !== 'string' || value.length === 0) { throw new Error(API_ENCRYPT_REQUEST_ERROR_MSG); } const encryptedValue = getEncryptor().encrypt(value); if (!encryptedValue) { throw new Error(API_ENCRYPT_REQUEST_ERROR_MSG); } return encryptedValue; } export function applyApiEncrypt(config: InternalAxiosRequestConfig) { const encryptConfig = config as ApiEncryptRequestConfig; if (encryptConfig.apiEncryptProcessed) { return config; } const encryptFields = getApiEncryptRule(config); if (!encryptFields) { return config; } if (!isPlainObject(config.data)) { throw new Error(API_ENCRYPT_REQUEST_ERROR_MSG); } const nextData = { ...config.data }; encryptFields.forEach(field => { nextData[field] = encryptFieldValue(nextData[field]); }); config.data = nextData; config.headers.set(API_ENCRYPT_HEADER, API_ENCRYPT_HEADER_VALUE); encryptConfig.apiEncryptProcessed = true; return config; }