| | |
| | | handleResponseError, |
| | | handleServiceResult, |
| | | } from './handle' |
| | | import { |
| | | aesEncrypt, |
| | | generateAesKey, |
| | | generateHeaders, |
| | | generateSign, |
| | | generateSignKey, |
| | | getTimestamp, |
| | | rsaEncrypt, |
| | | } from '@/utils/crypto' |
| | | import { v4 as uuidv4 } from 'uuid' |
| | | |
| | | export interface EncryptedRequestOptions<T = any> { |
| | | sensitiveFields?: string[] |
| | | rsaPublicKey?: string |
| | | secretKey?: string |
| | | meta?: any |
| | | } |
| | | |
| | | export interface EncryptedRequestConfig { |
| | | path: string |
| | | data?: any |
| | | options?: EncryptedRequestOptions |
| | | } |
| | | |
| | | const { onAuthRequired, onResponseRefreshToken } = createServerTokenAuthentication<VueHookType>({ |
| | | // 服务端判定token过期 |
| | |
| | | method.config.headers['Content-Type'] = 'application/x-www-form-urlencoded' |
| | | method.data = new URLSearchParams(method.data as URLSearchParams).toString() |
| | | } |
| | | |
| | | // 加密和签名逻辑 |
| | | const timestamp = getTimestamp() |
| | | const rsaPublicKey = method.meta?.rsaPublicKey || '' // 从meta中获取RSA公钥 |
| | | const secretKey = method.meta?.secretKey || '' // 从meta中获取签名密钥 |
| | | const sensitiveFields = method.meta?.sensitiveFields || [] // 从meta中获取敏感字段 |
| | | console.log('secretKey', secretKey) |
| | | console.log('method.meta', method.meta, 'method.data', method.data) |
| | | if (secretKey) { |
| | | if (method.data && typeof method.data === 'object') { |
| | | // 分离敏感字段和非敏感字段 |
| | | const sensitiveData: Record<string, any> = {} |
| | | const normalData: Record<string, any> = {} |
| | | |
| | | Object.keys(method.data).forEach((key) => { |
| | | if (sensitiveFields.includes(key)) { |
| | | sensitiveData[key] = method.data[key] |
| | | } |
| | | else { |
| | | normalData[key] = method.data[key] |
| | | } |
| | | }) |
| | | |
| | | // 加密敏感数据 |
| | | if (Object.keys(sensitiveData).length > 0 && rsaPublicKey) { |
| | | const aesKey = generateAesKey() |
| | | const encryptedAesKey = rsaEncrypt(aesKey, rsaPublicKey) |
| | | |
| | | // 将所有敏感字段一起加密成一个密文 |
| | | const encryptedData = aesEncrypt(JSON.stringify(sensitiveData), aesKey) |
| | | |
| | | // 替换敏感字段为加密后的数据(保持字段名不变) |
| | | const encryptedParams: Record<string, any> = {} |
| | | Object.keys(sensitiveData).forEach((key) => { |
| | | encryptedParams[key] = encryptedData |
| | | }) |
| | | |
| | | method.data = { |
| | | ...normalData, |
| | | ...encryptedParams, |
| | | } |
| | | |
| | | // 生成签名,使用加密后的数据 |
| | | const signParams = { |
| | | ...normalData, |
| | | ...encryptedParams, |
| | | } |
| | | const sign = generateSign(signParams, secretKey, timestamp) |
| | | |
| | | // 添加请求头 |
| | | const headers = generateHeaders(sign, encryptedAesKey, timestamp) |
| | | Object.assign(method.config.headers, headers) |
| | | } |
| | | else { |
| | | // 所有接口都需要签名,即使没有敏感数据 |
| | | const sign = generateSign(method.data, secretKey, timestamp) |
| | | // 添加签名相关请求头 |
| | | const headers = { |
| | | timestamp: timestamp.toString(), |
| | | nonce: secretKey, |
| | | sign, |
| | | } |
| | | |
| | | Object.assign(method.config.headers, headers) |
| | | } |
| | | } |
| | | else { |
| | | // 没有传参数,但仍然需要添加时间戳和签名 |
| | | // 对于无参数的请求,默认传一个时间戳的参数 |
| | | const paramsWithTimestamp = { |
| | | timestamp: timestamp.toString(), |
| | | } |
| | | // 使用包含时间戳的对象生成签名 |
| | | const sign = generateSign(paramsWithTimestamp, secretKey, timestamp) |
| | | console.log('method', method) |
| | | // 将时间戳作为参数传递 |
| | | method.data = paramsWithTimestamp |
| | | |
| | | // 添加签名相关请求头 |
| | | const headers = { |
| | | timestamp: timestamp.toString(), |
| | | nonce: secretKey, |
| | | sign, |
| | | } |
| | | Object.assign(method.config.headers, headers) |
| | | } |
| | | } |
| | | |
| | | alovaConfig.beforeRequest?.(method) |
| | | }), |
| | | responded: onResponseRefreshToken({ |
| | |
| | | }), |
| | | }) |
| | | } |
| | | |
| | | export async function createEncryptedRequest<T = any>( |
| | | requestInstance: any, |
| | | config: EncryptedRequestConfig, |
| | | ): Promise<Service.ResponseResult<T>> { |
| | | const { path, data, options = {} } = config |
| | | const { sensitiveFields = [], rsaPublicKey: providedPublicKey, secretKey: providedSecretKey, meta = {} } = options |
| | | |
| | | let rsaPublicKey = providedPublicKey |
| | | let secretKey = providedSecretKey |
| | | |
| | | if (sensitiveFields.length > 0 && !rsaPublicKey) { |
| | | secretKey = generateSignKey() |
| | | const rsaResult = await requestInstance.Post<Service.ResponseResult<string>>('/account/base/v1/getRsaPubKey', {}).send() |
| | | |
| | | if (!rsaResult.isSuccess || !rsaResult.data) { |
| | | throw new Error('获取RSA公钥失败') |
| | | } |
| | | |
| | | rsaPublicKey = rsaResult.data |
| | | } |
| | | |
| | | if (!secretKey) { |
| | | secretKey = generateSignKey() |
| | | } |
| | | |
| | | const methodInstance = requestInstance.Post<Service.ResponseResult<T>>(path, data || {}) |
| | | methodInstance.meta = { |
| | | ...meta, |
| | | rsaPublicKey, |
| | | secretKey, |
| | | sensitiveFields, |
| | | } |
| | | |
| | | return methodInstance.send() |
| | | } |