/** * 独立的注入脚本 - 在页面上下文中最先执行 * 这个文件会被注入到页面上下文中,确保在任何页面脚本运行前拦截 */ (function () { 'use strict'; // 立即拦截,不等待任何其他代码 console.log('🚀 [页面上下文] 拦截脚本开始执行 - 时间:', new Date().toISOString()); // 在页面上下文中存储解密记录 if (!window.__XHR_DECRYPT_DATA__) { window.__XHR_DECRYPT_DATA__ = { requests: [], addRequest: function (record) { this.requests.push(record); if (this.requests.length > 100) { this.requests.shift(); } // 通知 content script window.dispatchEvent(new CustomEvent('__XHR_DECRYPTED__', { detail: record })); }, getRequests: function () { return this.requests; }, clear: function () { this.requests = []; } }; } const decryptedData = window.__XHR_DECRYPT_DATA__; const requestHeadersMap = new Map(); const skipDecryptPaths = ['v1/picture/upload']; // 获取解密工具 function getDecryptTools() { if (window.__DECRYPT_TOOLS__) { return window.__DECRYPT_TOOLS__; } return null; } // 计算密钥 function calculateKey(timestamp, traceId) { if (!timestamp || !traceId) return null; return String(timestamp + traceId).slice(0, 16); } // 获取解密函数 function getDecryptFunction() { const tools = getDecryptTools(); if (tools && tools.Decrypt) { return tools.Decrypt; } return null; } // 解密请求数据 function decryptRequestData(encryptedData, timestampKey) { try { const Decrypt = getDecryptFunction(); if (!Decrypt) { console.warn('[页面上下文] 无法获取解密函数'); return encryptedData; } if (encryptedData && encryptedData.data && typeof encryptedData.data === 'string') { const decrypted = Decrypt(encryptedData.data, timestampKey); return JSON.parse(decrypted); } return encryptedData; } catch (error) { console.error('[页面上下文] 解密请求数据失败:', error); return encryptedData; } } // 解密响应数据 function decryptResponseData(encryptedData, timestampKey) { try { const Decrypt = getDecryptFunction(); if (!Decrypt) { console.warn('[页面上下文] 无法获取解密函数'); return encryptedData; } if (typeof encryptedData === 'string') { const decrypted = Decrypt(encryptedData, timestampKey); try { return JSON.parse(decrypted); } catch (e) { if (window.JSON5) { return window.JSON5.parse(decrypted); } return decrypted; } } return encryptedData; } catch (error) { console.error('[页面上下文] 解密响应数据失败:', error); return encryptedData; } } // 获取密钥 function getTimestampKey(url, uuid, requestHeaders) { const tools = getDecryptTools(); if (tools && tools.getKey) { const key = tools.getKey(url, uuid); if (key) return key; } const timestamp = requestHeaders?.time || requestHeaders?.Time || ''; const traceId = requestHeaders?.TraceId || requestHeaders?.['trace-id'] || ''; if (timestamp && traceId) { return calculateKey(timestamp, traceId); } return null; } // ========== 拦截 Fetch (优先,因为 umi-request 使用 fetch) ========== if (typeof fetch !== 'undefined' && window.fetch) { const originalFetch = window.fetch; window.fetch = function (...args) { const [url, options = {}] = args; const method = options.method || 'GET'; const requestId = Date.now() + Math.random(); const shouldSkip = skipDecryptPaths.some(path => url.includes(path)); const originalBody = options.body; console.log('🔍 [页面上下文] 拦截 Fetch:', method, url); return originalFetch.apply(this, args).then(async (response) => { if (shouldSkip) return response; try { const clonedResponse = response.clone(); const uuid = clonedResponse.headers.get('uuid'); if (!uuid) { return response; } const headers = options.headers || {}; const timestamp = headers.time || headers.Time || ''; const traceId = headers.TraceId || headers['trace-id'] || ''; const timestampKey = getTimestampKey(url, uuid, { time: timestamp, TraceId: traceId }); if (!timestampKey) { console.warn('[页面上下文] 无法获取密钥 (Fetch)', { url, uuid }); return response; } // 解密请求数据 let requestBody = null; if (originalBody) { try { if (typeof originalBody === 'string') { const parsed = JSON.parse(originalBody); requestBody = decryptRequestData(parsed, timestampKey); } else { requestBody = originalBody; } } catch (e) { requestBody = originalBody; } } // 解密响应数据 let responseData = null; try { const text = await clonedResponse.text(); if (text) { try { responseData = JSON.parse(text); } catch (e) { responseData = text; } } } catch (e) { console.error('[页面上下文] 读取响应数据失败:', e); } const decryptedResponse = decryptResponseData(responseData, timestampKey); // 记录解密结果 const decryptedRecord = { id: requestId, url: url, method: method, uuid: uuid, timestampKey: timestampKey, request: requestBody, response: decryptedResponse, timestamp: new Date().toISOString() }; // 存储到页面上下文 decryptedData.addRequest(decryptedRecord); // 输出到控制台 console.group('🔓 Fetch 解密 [' + method + '] ' + url); console.log('🔑 密钥:', timestampKey); console.log('📤 请求:', requestBody); console.log('📥 响应:', decryptedResponse); console.log('📍 UUID:', uuid); console.groupEnd(); } catch (error) { console.error('[页面上下文] Fetch 解密过程出错:', error); } return response; }).catch((error) => { console.error('[页面上下文] Fetch 请求失败:', error); return Promise.reject(error); }); }; console.log('✅ [页面上下文] Fetch 拦截已设置'); } // ========== 拦截 XMLHttpRequest ========== if (typeof XMLHttpRequest !== 'undefined') { const originalOpen = XMLHttpRequest.prototype.open; const originalSend = XMLHttpRequest.prototype.send; const originalSetRequestHeader = XMLHttpRequest.prototype.setRequestHeader; XMLHttpRequest.prototype.open = function (method, url, ...args) { this._method = method; this._url = url; this._requestHeaders = {}; requestHeadersMap.set(this, {}); console.log('🔍 [页面上下文] 拦截 XHR open:', method, url); return originalOpen.apply(this, [method, url, ...args]); }; XMLHttpRequest.prototype.setRequestHeader = function (name, value) { if (!this._requestHeaders) { this._requestHeaders = {}; } this._requestHeaders[name] = value; const headers = requestHeadersMap.get(this) || {}; headers[name] = value; requestHeadersMap.set(this, headers); return originalSetRequestHeader.apply(this, [name, value]); }; XMLHttpRequest.prototype.send = function (body) { const url = this._url; const method = this._method; const requestId = Date.now() + Math.random(); const shouldSkip = skipDecryptPaths.some(path => url.includes(path)); // 监听响应 this.addEventListener('loadend', function () { if (shouldSkip) return; try { const headers = requestHeadersMap.get(this) || {}; const uuid = this.getResponseHeader('uuid'); if (!uuid) { return; } const timestampKey = getTimestampKey(url, uuid, headers); if (!timestampKey) { console.warn('[页面上下文] 无法获取密钥', { url, uuid, headers }); return; } // 解密请求数据 let requestBody = null; if (body) { try { const parsed = typeof body === 'string' ? JSON.parse(body) : body; requestBody = decryptRequestData(parsed, timestampKey); } catch (e) { requestBody = body; } } // 解密响应数据 let responseData = null; try { if (this.responseText) { responseData = JSON.parse(this.responseText); } } catch (e) { responseData = this.responseText || this.response; } const decryptedResponse = decryptResponseData(responseData, timestampKey); // 记录解密结果 const decryptedRecord = { id: requestId, url: url, method: method, uuid: uuid, timestampKey: timestampKey, request: requestBody, response: decryptedResponse, timestamp: new Date().toISOString() }; // 存储到页面上下文 decryptedData.addRequest(decryptedRecord); // 输出到控制台 console.group('🔓 XHR 解密 [' + method + '] ' + url); console.log('🔑 密钥:', timestampKey); console.log('📤 请求:', requestBody); console.log('📥 响应:', decryptedResponse); console.log('📍 UUID:', uuid); console.groupEnd(); } catch (error) { console.error('[页面上下文] 解密过程出错:', error); } }); return originalSend.apply(this, [body]); }; console.log('✅ [页面上下文] XHR 拦截已设置'); } // 暴露 API 供 DevTools 面板使用 window.__XHR_DECRYPT_EXTENSION__ = { getDecryptedRequests: function () { return decryptedData.getRequests(); }, clearDecryptedRequests: function () { decryptedData.clear(); console.log('✅ [页面上下文] 已清空解密记录'); } }; console.log('✅ [页面上下文] 拦截脚本初始化完成'); console.log('✅ [页面上下文] 已拦截 Fetch:', typeof window.fetch !== 'undefined'); console.log('✅ [页面上下文] 已拦截 XHR:', typeof XMLHttpRequest !== 'undefined'); })();