diff --git a/src/constants/app-constants.ts b/src/constants/app-constants.ts index cea6637a..40f1082a 100644 --- a/src/constants/app-constants.ts +++ b/src/constants/app-constants.ts @@ -3,3 +3,6 @@ export const APP_REQUEST_HEADER_CODE = 'X-App-Code'; /**应用-请求头-系统版本 */ export const APP_REQUEST_HEADER_VERSION = 'X-App-Version'; + +/**应用-请求数据-密钥 */ +export const APP_DATA_API_KEY = 'T9ox2DCzpLfJIPzkH9pKhsOTMOEMJcFv'; diff --git a/src/constants/result-constants.ts b/src/constants/result-constants.ts index e2bccbd6..ae7dfeeb 100644 --- a/src/constants/result-constants.ts +++ b/src/constants/result-constants.ts @@ -1,3 +1,12 @@ +/**响应-code加密数据 */ +export const RESULT_CODE_ENCRYPT = 2; + +/**响应-msg加密数据 */ +export const RESULT_MSG_ENCRYPT: Record = { + zh_CN: '加密!', + en_US: 'encrypt!', +}; + /**响应-code正常成功 */ export const RESULT_CODE_SUCCESS = 1; diff --git a/src/plugins/http-fetch.ts b/src/plugins/http-fetch.ts index 9ea0ecab..09d0b96d 100644 --- a/src/plugins/http-fetch.ts +++ b/src/plugins/http-fetch.ts @@ -13,10 +13,13 @@ import { import { APP_REQUEST_HEADER_CODE, APP_REQUEST_HEADER_VERSION, + APP_DATA_API_KEY, } from '@/constants/app-constants'; import { + RESULT_CODE_ENCRYPT, RESULT_CODE_ERROR, RESULT_CODE_SUCCESS, + RESULT_MSG_ENCRYPT, RESULT_MSG_ERROR, RESULT_MSG_NOT_TYPE, RESULT_MSG_SERVER_ERROR, @@ -25,11 +28,12 @@ import { RESULT_MSG_URL_NOTFOUND, RESULT_MSG_URL_RESUBMIT, } from '@/constants/result-constants'; +import { decryptAES, encryptAES } from '@/utils/encrypt-utils'; /**响应结果类型 */ export type ResultType = { /**响应码 */ - code: number | 1 | 0; + code: number; /**信息 */ msg: string; /**数据 */ @@ -76,6 +80,8 @@ type OptionsType = { body?: BodyInit; /**防止数据重复提交 */ repeatSubmit?: boolean; + /**接口数据加密 */ + crypto?: boolean; /**携带授权Token请求头 */ whithToken?: boolean; /**中断控制信号,timeout不会生效 */ @@ -167,24 +173,40 @@ function beforeRequest(options: OptionsType): OptionsType | Promise { // 请求拼接地址栏参数 if (options.params) { - let paramStr = ''; const params = options.params; + const queryParams: string[] = []; for (const key in params) { const value = params[key]; // 空字符或未定义的值不作为参数发送 if (value === '' || value === undefined) continue; - paramStr += `&${encodeURIComponent(key)}=${encodeURIComponent(value)}`; + const str = `${encodeURIComponent(key)}=${encodeURIComponent(value)}`; + queryParams.push(str); } - if (paramStr && paramStr.startsWith('&')) { - options.url = `${options.url}?${paramStr.substring(1)}`; + const paramStr = queryParams.join('&'); + if (paramStr) { + const separator = options.url.includes('?') ? '&' : '?'; + // 请求加密 + if (options.crypto) { + debugger; + const data = encryptAES(JSON.stringify(paramStr), APP_DATA_API_KEY); + options.url += `${separator}data=${encodeURIComponent(data)}`; + } else { + options.url += `${separator}${paramStr}`; + } } } // 非get参数提交 - if (options.data instanceof FormData) { - options.body = options.data; - } else { - options.body = JSON.stringify(options.data); + let body = options.data + if (body instanceof FormData) { + options.body = body; + } else if (body) { + // 请求加密 + if (options.crypto) { + const data = encryptAES(JSON.stringify(body), APP_DATA_API_KEY); + body = { data }; + } + options.body = JSON.stringify(body); } return options; } @@ -199,6 +221,28 @@ function interceptorResponse(res: ResultType): ResultType | Promise { window.location.reload(); } + // 响应数据解密 + if (res.code === RESULT_CODE_ENCRYPT) { + const str = decryptAES(res.data, APP_DATA_API_KEY); + let data = {}; + try { + data = JSON.parse(str); + } catch (error) { + console.error(error); + } + if (Object.keys(data).length === 0) { + return Promise.resolve({ + code: RESULT_CODE_ERROR, + msg: RESULT_MSG_ENCRYPT[language], + }); + } + return Promise.resolve({ + code: RESULT_CODE_SUCCESS, + msg: RESULT_MSG_SUCCESS[language], + data, + }); + } + // 风格处理 if (!Reflect.has(res, 'code')) { return Promise.resolve({ @@ -266,7 +310,7 @@ export async function request(options: OptionsType): Promise { case 'text': // 文本数据 const str = await res.text(); return { - code: 1, + code: RESULT_CODE_SUCCESS, msg: str, }; case 'json': // json格式数据 diff --git a/src/utils/encrypt-utils.ts b/src/utils/encrypt-utils.ts new file mode 100644 index 00000000..351d125c --- /dev/null +++ b/src/utils/encrypt-utils.ts @@ -0,0 +1,50 @@ +import CryptoJS from 'crypto-js'; +import { isValid, decode } from 'js-base64'; + +/** + * AES 加密并转为 base64 + * @param plaintext 数据字符串 + * @param aeskey 密钥 + * @returns 加密字符串 + */ +export function encryptAES(plaintext: string, aeskey: string): string { + const nowRoaund = new Date().getTime().toString(6); + const key = CryptoJS.enc.Utf8.parse(aeskey); + const iv = CryptoJS.enc.Utf8.parse(nowRoaund); + const encrypted = CryptoJS.AES.encrypt(`${nowRoaund}${plaintext}`, key, { + iv: iv, + blockSize: 16, + mode: CryptoJS.mode.CBC, + padding: CryptoJS.pad.Pkcs7, + format: CryptoJS.format.OpenSSL, + }); + return encrypted.toString(); +} + +/** + * AES 解密 + * @param ciphertext 加密字符串 + * @param aeskey 密钥 + * @returns 数据字符串 + */ +export function decryptAES(ciphertext: string, aeskey: string): string { + const nowRoaund = new Date().getTime().toString(6); + const key = CryptoJS.enc.Utf8.parse(aeskey); + const iv = CryptoJS.enc.Utf8.parse(nowRoaund); + const decrypted = CryptoJS.AES.decrypt(ciphertext, key, { + iv: iv, + blockSize: 16, + mode: CryptoJS.mode.CBC, + padding: CryptoJS.pad.Pkcs7, + format: CryptoJS.format.OpenSSL, + }); + const base64Str = decrypted.toString(CryptoJS.enc.Base64); + if (isValid(base64Str)) { + const str = decode(base64Str); + const idx = str.indexOf(':)', 10); + if (idx > 10) { + return str.substring(idx + 2); + } + } + return ''; +}