import $cache from '@/plugins/cache'; import useUserStore from '@/store/modules/user'; import request from '@/utils/request'; import axios from 'axios'; import sign from '@/utils/sign.js'; import CryptoJS from 'crypto-js'; /** * 包装 request 请求,统一使用 Go 服务地址作为 baseURL * @param config 原始请求配置 */ const BASE_GO_URL = import.meta.env.VITE_APP_BASE_API_GO; const token = $cache.local.get('goToken'); interface RequestGo extends Function { (config: any): Promise; download?: (url: string, params: any, filename: string) => void; } // 加密密钥 const corySecretKey = 'happyCoryOrTieHanHan202410151415'; // 配置新建一个 axios 实例 const service = axios.create({ baseURL: BASE_GO_URL as any, timeout: 120000, headers: { 'Content-Type': 'application/json' } }); // 不需要修改项目id的接口数组 const whiteUrl = [ '/api/wxApplet/wxApplet/sysProjectTeam/list', '/api/wxApplet/wxApplet/sysProjectTeam/add', '/api/wxApplet/wxApplet/sysProjectTeam/edit', '/api/v1/test/testFollowInfo/add', '/api/wxApplet/wxApplet/busConstructionUser/changePay', // 获取项目资料文件夹 '/api/v1/system/documentData/treeStructureData', '/api/v1/system/busConstructionUser/exportSalary', '/api/v1/system/busSalaryDetails/list', '/api/v1/system/ys7Devices/add', '/api/v1/system/ys7Devices/edit', '/api/v1/system/subProject/add', '/api/v1/system/subProject/edit', '/api/v1/system/subProject/list', '/api/v1/system/workStatus/getTree', '/api/v1/system/sysProjectIntroduce/list', '/api/v1/system/sysProjectIntroduce/add', '/api/v1/system/sysProjectIntroduce/edit', '/video/hat/manage/api/v1/video/device/list', '/video/hat/manage/api/v1/video/project/bind', '/api/v1/system/notifications/publish', '/api/v1/system/notifications/list', '/api/v1/system/notifications/edit', '/webodm/api/v1/taskCreate', '/webodm/api/v1/taskProcess', '/webodm/api/v1/download', '/api/v1/system/manageTaskRecord/upDataResource', '/api/v1/system/ys7Devices/list', '/api/v1/system/busFolderFile/add', '/api/v1/system/manageTaskRecordResource/voluntarilyReq', '/api/v1/system/busAttendanceMachine/edit', '/api/v1/system/qianqiFangzhen/add', '/api/v1/system/qianqiNibianqi/add' ]; const exceptionStr = '/api/v1/test/'; // /api/v1/test/*接口拦截 // 添加请求拦截器 service.interceptors.request.use( (config: any) => { // 在发送请求之前做些什么 token if (token) { config.headers = config.headers || {}; (config.headers as any)['Authorization'] = `Bearer ${token}`; console.log('🚀 ~ config.headers:', config.headers); } const stores = useUserStore(); if (!whiteUrl.includes(config.url) && !config.url.includes(exceptionStr)) { if (config.params && Reflect.has(config.params, 'projectId')) { config.params.projectId = stores.selectedProject.goId; } // 处理FormData中的projectId if (config.data instanceof FormData && config.data.has('projectId')) { config.data.set('projectId', stores.selectedProject.goId); } } let dataInfoReq = {}; if (config.method === 'get' || config.method === 'delete') { config.params = config.params || {}; dataInfoReq = JSON.parse(JSON.stringify(config.params)); const encryptedParam = encryptAES256(JSON.stringify(dataInfoReq), corySecretKey); if ($cache.local.get('i18n') != null) { config.params = { coryKey: encryptedParam, corySimplifiedToTraditional: $cache.local.get('i18n') }; } else { config.params = { coryKey: encryptedParam }; } } else { // 处理FormData类型 if (config.data instanceof FormData) { // 将FormData转换为普通对象 dataInfoReq = Object.fromEntries(config.data.entries()); // 重新创建一个新的FormData实例 const newFormData = new FormData(); const encryptedData = encryptAES256(JSON.stringify(dataInfoReq), corySecretKey); newFormData.append('coryKey', encryptedData); if ($cache.local.get('i18n') != null) { newFormData.append('corySimplifiedToTraditional', $cache.local.get('i18n')); } // 如果有文件,需要重新添加 for (let [key, value] of config.data.entries()) { if (value instanceof File) { newFormData.append(key, value); } } config.data = newFormData; // 设置Content-Type为undefined,让浏览器自动设置boundary config.headers['Content-Type'] = undefined; } else { dataInfoReq = config.data; const encryptedData = encryptAES256(JSON.stringify(dataInfoReq), corySecretKey); if ($cache.local.get('i18n') != null) { config.data = { coryKey: encryptedData, corySimplifiedToTraditional: $cache.local.get('i18n') }; } else { config.data = { coryKey: encryptedData }; } } } const { timestamp, nonce, sign: signature } = sign(dataInfoReq); config.headers.timestamp = timestamp; config.headers.nonce = nonce; config.headers.sign = signature; return config; }, (error) => { // 对请求错误做些什么 return Promise.reject(error); } ); // 添加响应拦截器 service.interceptors.response.use( (response) => { // 对响应数据进行解密操作 try { // 处理特殊情况:data为空值或不需要解密的情况 if (response.data.data === null || response.data.data === '' || response.data.data === undefined) { return response.data; } // 检查响应数据格式 if (typeof response.data.data !== 'string') { // 尝试将非字符串数据转换为字符串 try { response.data.data = JSON.stringify(response.data.data); } catch (convertError) { throw new Error('响应数据格式不正确,无法转换为字符串'); } } // 执行解密 const decryptedData = decryptAES256(response.data.data, corySecretKey); // 正确处理解密后的数据 if (decryptedData && typeof decryptedData === 'object') { // 保持response.data的结构不变,只替换data字段的内容 response.data = { ...response.data, data: decryptedData }; } else { // 如果解密后的数据不是对象,可能需要特殊处理 response.data.data = decryptedData; } if (response.data.code !== 0) { ElMessage.error(response.data.message); } return response.data; } catch (decryptError) { // 提供更友好的错误信息 if (decryptError.message.includes('无效的Base64格式')) { ElMessage.error('数据格式错误:接收到无效的加密数据'); } else { ElMessage.error('数据解密失败,请联系管理员'); } return Promise.reject(decryptError); } }, (error) => { // 对响应错误做点什么 if (error.message.indexOf('timeout') !== -1) { ElMessage.error('网络超时'); } else if (error.message === 'Network Error') { ElMessage.error('网络连接错误'); } else { console.log(error, '网络错误'); if (error.response.data) ElMessage.error(error.response.statusText); else { ElMessage.error('接口路径找不到'); console.log(error, '接口路径找不到'); } } return Promise.reject(error); } ); // 加密函数 function encryptAES256(plainText: string, key: string): string | null { try { const parsedKey = CryptoJS.enc.Utf8.parse(key); const iv = CryptoJS.lib.WordArray.random(16); const plainTextWordArray = CryptoJS.enc.Utf8.parse(plainText); const encrypted = CryptoJS.AES.encrypt(plainTextWordArray, parsedKey, { iv: iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 }); const encryptedData = iv.concat(encrypted.ciphertext); return encryptedData.toString(CryptoJS.enc.Base64); } catch (err) { console.error('Encryption error:', err.message); return null; } } // 增强的解密函数,处理各种输入情况 function decryptAES256(encryptedData: string, key: string): any { // eslint-disable-next-line no-useless-catch try { // 检查输入是否为有效的Base64字符串 if (!/^[A-Za-z0-9+/=]+$/.test(encryptedData)) { // 尝试对非标准Base64字符串进行处理 // 移除可能的前缀 const cleanData = encryptedData.replace(/^data:.*?;base64,/, '').replace(/\s/g, ''); // 移除空格 if (!/^[A-Za-z0-9+/=]+$/.test(cleanData)) { throw new Error('无效的Base64格式'); } return decryptAES256(cleanData, key); } const decodedData = CryptoJS.enc.Base64.parse(encryptedData); const iv = CryptoJS.lib.WordArray.create(decodedData.words.slice(0, 4)); const encryptedText = CryptoJS.lib.WordArray.create(decodedData.words.slice(4)); const parsedKey = CryptoJS.enc.Utf8.parse(key); const decrypted = CryptoJS.AES.decrypt({ ciphertext: encryptedText }, parsedKey, { iv: iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 }); const decryptedStr = decrypted.toString(CryptoJS.enc.Utf8); // 尝试解析为JSON try { return JSON.parse(decryptedStr); } catch (jsonError) { return decryptedStr; } } catch (err) { throw err; } } // 导出 axios 实例 export default service;