first commit
This commit is contained in:
345
inject-script.js
Normal file
345
inject-script.js
Normal file
@@ -0,0 +1,345 @@
|
||||
/**
|
||||
* 独立的注入脚本 - 在页面上下文中最先执行
|
||||
* 这个文件会被注入到页面上下文中,确保在任何页面脚本运行前拦截
|
||||
*/
|
||||
|
||||
(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');
|
||||
})();
|
||||
|
||||
Reference in New Issue
Block a user