案例地址链接:https://search.jd.com/Search?keyword=白酒\&enc=utf-8\&pvid=53c39759079d4b92a3e6d3ea71974571
一、案例【京东载荷h5st字段】
1.1、入口定位

这次的目标很明确,我们就是需要破解京东的搜索接口的h5st字段,首先我们先通过关键字进行搜索,搜索h5st


首先我们先找到我们的目标url复制到【https://curlconverter.com/】中生成基础爬虫代码

很明显,人家是做了加密处理,根本不给你返回任何的数据,所以我们只有破解h5st才可以获取到数据
1.2、代码分析

点击进入到代码内部

进来以后这里就两行代码,其中第二行【return tP.resolve(_Ga);】就是包装,把这个带有h5st的值直接包装到promise中去,所以关键点还是第一行代码【var Ga = this._sdnmd(Gf)】中的this._sdnmd($Gf)就生成了h5st了
【这一步直接出结果就和promise没关系了,下一步就是包装promise】

window.PSign.sign(d).then(res=>{console.log(res)})
这行代码会生成一个promise,所以我们执行上面的这行代码就会生成promise的结果
1.3、扣JS

因为主要是this._sdnmd(_Gf); 这句代码生成的h5st所以把这句代码直接扣走
参数_Gf是直接传递来的,所以我们拼接_Gf,进行测试,缺什么补什么

由此可得d就是参数_$Gf,所以进行拼接即可

在扣完代码拼接完paramsH5sign和params以后,我们就需要确定调用this._sdnmd中的this 其实就是window.ParamsSign = _Gk,
所以我们可以进行替换,
参数加密处理的逻辑就是,先对参数params进行SHA256处理,然后把处理完的结果放到paramsH5sign.body中,然后在对paramsH5sign参数进行处理生成h5st
所以我们替换完this以后,我们需要找到window.ParamsSign

通过判断是new出来的,所以我们只需要扣try中的正常处理逻辑即可

当把所有的代码都扣出来完成以后,允许就会报window找不到,就补window
这次又报document找不到,我们可以在补document,补完了又会报Element
这样其实我们第一阶段就已经完成了,下面我们就需要开始链式补环境

当我们把window和document还有Element补完以后,结果就会出来了,就有h5st这个结果了,但是这个结果是错误的,在python中运行是得不到结果的,因为没过服务器的检测点,并且这个和h5st的长度也没关系,因为这个代码中他们做了原型链的检测,所以和长度什么的没关系,
所以下面我们还是需要分析原型链关系,根据原型链关系进行补环境

看这个div标签的属性和方法,在属性和方法中找它的原型链关系

通过查看可以得到Element继承Node ,Node继承EventTarget, EventTarget 继承Object

如果提示报错,我们不知道如何处理的时候,我们就复制报错的地方,在源码中找到,断点定位到该处,进行判断是什么报错找不到,处理使用代码以外还需要会断点调试什么的,进行查错,
1.4、补环境【 补环境框架,V8引擎】
有的时候在源码中,他们会判断,Element是否有,并且有的时候也会判断Element的原型链它的父亲是否有,Element它的爷爷是否有,所以我们需要通过原型链去补环境,而不能直接简单的补一下
// 补环境
window = {}
window.document = {}
function Element(){
}
有些网站它不检测原型链,我们按照上面的方式补环境是可以的,但是当有些网站检测原型链的时候,我们在按照上面的基础补环境就不行了



以上是一段准备代码,方便我们通过链式补环境,
// 二、补环境
// (1) window对象处理
global_val = globalThis // globalThis 其实就是global 就是node中的顶级变量
// 给global加一个window属性哈 给global全局配一个window属性 给global顶级变量挂载一个全局变量window
Object.defineProperty(global_val, "window", {
get: function () {
return global_val
},
set: function set_window(val) {
global_val = val
},
enumerable: true,
configurable: true,
})
// 无论代码调取那个都是指向这个window 做一个全局处理
window.top = window.self = window.window = window
以上是对window缺少找不到进行的补环境处理

小window --》 大Window --》 WindowProperties --》 EventTarget --》 Object
小window其实是来自于大Window的一个实例化对象,小window的原型对象其实就是大Window
所以我们如果都补全了最好就是四层,目前我们该案例就写了两层
// 四层都不齐全
createConstructor("EventTarget", true, [], {})
createConstructor("WindowProperties", true, [], {},"EventTarget" )
createConstructor("Window", true, [], {},"WindowProperties" )
// 只补两层
createConstructor("Window", true, [], {})

window.document 报document找不到,因为document中的东西太多,所以我们先简单的补一下
window.document = {} 先跳过

我们先window.document = {} 跳过document ,先处理Element
Element 首字母大写的,我们都可以当作是类来处理,查看它的原型链
直接dir(Element),查看一下它爹,它爷这些原型链即可

/*
* Element --》 Node --》 EventTarget --》 Object
* 因为Element的父类是Node,Node的父类是EventTarget,EventTarget的父类Object,而且在createConstructor方法中,每次在最后面也只能添加一层的父类
* 所以我们给Element设置完父类Node以后,还需要给Node以同样的方式设置父类EventTarget 连续三个createConstructor,这才完成了一个原型串
* */
createConstructor("EventTarget", true, [], {}) // EventTarget 后面就没爹了,没爹不用写,默认是Object
createConstructor("Node", true, ["parentNode", "childNodes"], {}, "EventTarget")
createConstructor("Element", true, ["childElementCount", "innerHTML"], {}, "Node")

到这里,已经没有任何缺少环境的信息提示了,报一个看不懂对象的错误,所以这里我们就需要去添加监控了

添加完监控以后,就会吐很多关于window相关的信息,但是这些window相关的信息都没什么用
所以我们还需要监听另外的一个,需要监控document。

实例化出错了
因为我们刚开始肯定是不知道,那个需要补,那个不需要补,但是现在假装我们已经知道了需要补那个环境,所以我们就挑重点的来补

首先我们看到document调用了all
所以我们还是先dir(document.all)


// 补document的爹和爷 document --》 HTMLDocument --》 Document --》 node
createConstructor("Document", true, [], {}, "Node")
createConstructor("HTMLDocument", true, [], {}, "Document")
// 这里是document实例化的时候传入的一些属性
// all:new HTMLAllCollection(null,null,"ljc") 其实这样就实例化一个对象就可以了,但是还有一种可能是document.all.xxx 所以我们也需要在实例化的同时也监控一下
// 所以我们就watch(new HTMLAllCollection(null,null,"ljc"),谁调用的呀)document.all.xxx,也就被监控了
all:watch(new HTMLAllCollection(null,null,"ljc"),"document.all")
下一个就是dir(document.documentElement)


// 补document.documentElement HTMLHtmlElement --》 HTMLElement --》 Element --》 Node --》 EventTarget --》 Object
createConstructor("HTMLElement", true, [], {},"Element")
createConstructor("HTMLHtmlElement", true, [], {},"HTMLElement")
//按照上面的方式补全documentElement
documentElement:watch(new HTMLHtmlElement(null,null,"ljc"),"document.documentElement")
下面在补一个cookie属性

下一个dir(document.body)


// 补document.body HTMLBodyElement --> HTMLElement --> Element --> Node --> EventTarget --> Object
createConstructor("HTMLBodyElement", true, [], {}, "HTMLElement")
//补body
body: watch(new HTMLBodyElement(null, null, "ljc"), "document.body")
到目前为止,document的属性就都补完了,现在开始补方法了

经过dir查看他们的链接关系,发现这些方法都在document这个类中,所以这些方法在那个类中,我们就应该补到那个类中
其中第一个优先补script
我们需要先查看他的链式关系


//补script
createConstructor("HTMLScriptElement", true, [], {}, "HTMLElement")
//补parentNode
createConstructor("HTMLHeadElement", true, [], {}, "HTMLElement")
createElement: function (tagName) {
// 打印一下tagName 看一下创建的是那个标签
console.log("Document prototype createElement ", tagName)
if (tagName === "script") {
// 因为script后面也有可能调用了别的,所以我们在这里也需要进行监控
script = watch(new HTMLScriptElement({
parentNode: watch(new HTMLHeadElement(null, null, "ljc"), "script的parentNode")
}, null, "ljc"), "script对象")
return script
}
// canvas和上面的script就一样了
if (tagName === "canvas") {
return {}
}
},

到这里,这个环境中已经有了script标签,虽然script调用这个parentNode找不到,但是至少不在报错了
所以我们这里就需要找这个parentNode是什么东西

因为这个ele是咱们自己创建的,没有放到任何的一个父级标签中,所以ele.parent是null


因为这个srcipt在调用parent之前调用了什么,比如text/javascript,所以这个script标签都在head中,但是body中可能也有,所以我们就需要多在源码中搜索一下确定一下
所以我们在补这个parent的时候,这个script.parentNode就是那个head标签,因为script标签在head中,所以我们就需要确定这个head标签中的第0元素,然后确定它的创建类,来创建这个parentNode


再往下就是补canvas 但是这个canvas可补,可不补,所以不需要关注
然后这个时候在运行,需要提前登录在运行,就会得到h5st 长度是556位,目前这个值是可以成功的



把paramsH5sign作为参数传递进去,然后通过开通的接口就会生成h5st,所以以后在想拿h5st就不需要在和Python打交道了直接做成一个接口,之前用Python拿数据都是使用import execjs或者import subprocess 之前都是用Python调度js代码,如果有了定时器什么的就比较麻烦,所以现在就直接把JS生成接口,如果Python要用,就直接自己传参自己拿结果,访问一次获取一个h5st,每次获取的h5st还都不一样,因为是时间作为了参数获取的h5st
1.5、代码文件
该案例需要导入:pip install curl_cffi npm install express
1.5.1、Python文件
import hashlib
from curl_cffi import requests
import time
import os
def sha256_hash(data: str) -> str:
"""
计算字符串的 SHA-256 哈希值
:param data: 输入字符串
:return: 十六进制表示的哈希值字符串
"""
hash_obj = hashlib.sha256(data.encode('utf-8'))
return hash_obj.hexdigest()
cookies = {
'__jdv': '143920055|direct|-|none|-|1759198339989',
'__jdu': '1759198339988240393195',
'3AB9D23F7A4B3CSS': 'jdd0336CXCJW76HVLG5NN3HX7RRXSOAKV7PM4O2R2CEWREEYEERJ3MGNRRZZL6AKZQDEG6JSLQTBE5CCGNYDHCKPS4VKR4EAAAAMZTBSF5TAAAAAADED2AUUGHUOS44X',
'mba_muid': '1759198339988240393195',
'wlfstk_smdl': 'di09prnlss3k3bnvofmpwmkjo812881l',
'TrackID': '1RVWFVwuwegh6iaq2dUaSUyxtlv2W8ZsWmxP21tBeuzA6JLP01W25RW4ZNKGWhDFKBR_xMLfaTohtTorBRBpdNlxBRXHQ87UD-YA5CjBNN54',
'thor': '2DD9CF08CB217E996764A091B245EB29B43DEECE308DCF74BCBC66126340B7CC57EDD320176A72EA74043C4CDD7F4324D872C1E3813F23CFC94796AE1ABFB72BC0EB76990CBC9F42DD82C52859CD62E4DF2374343ED9606AFB4437FA8D8FACB1753AD87ACE74EDE1B970E782C73FFEFB25EBC28E00E7D303C43E5B5FDC1C3826210EFE4E1F9CC0F684515F5703EB90D7302414E086525715F186A95323BEAE81',
'light_key': 'AASBKE7rOxgWQziEhC_QY6yaKYl0-nkwHhCq7N-FMTwrP69jFjUruev7Rsm2eNMxzUBY6VIP',
'pinId': 'w8Ih7LOJnxwxhZalSpUGsbV9-x-f3wj7',
'pin': 'jd_52b15ac66b44e',
'unick': '%E5%91%86%E5%91%867115',
'ceshi3.com': '203',
'_tp': 'oRxO98xexSXw5ZlTXC2fbDrx5URIy94AUL5RT79KEbs%3D',
'_pst': 'jd_52b15ac66b44e',
'__jdc': '143920055',
'areaId': '5',
'shshshfpa': 'b9a6c825-8808-1785-a164-cdcc6d85da4a-1759198380',
'shshshfpx': 'b9a6c825-8808-1785-a164-cdcc6d85da4a-1759198380',
'ipLoc-djd': '1-72-55653-0',
'shshshfpb': 'BApXSoJBtm_xAUGY1ek_5MROtpLvWQ2uQBhtYhK9o9xJ1MhEHxo62',
'3AB9D23F7A4B3C9B': '36CXCJW76HVLG5NN3HX7RRXSOAKV7PM4O2R2CEWREEYEERJ3MGNRRZZL6AKZQDEG6JSLQTBE5CCGNYDHCKPS4VKR4E',
'flash': '3_6gpgATZhoG6k8czgYn2HnA7xPO3mh2cXAmEzCQ0z422kecKRpx89Biu_-2dtCY3V_jARZ4wIxT3UKzcCONXY1gr97YLyPbCHYjhWNhvMGQGBq_IB6Udnm-kw105qUMUkmMKnu5lY-1pCuNNxyfD0jxFU2_-xm1QimzdddFQTJfmsemqUvJBa3V**',
'__jda': '143920055.1759198339988240393195.1759198340.1759198340.1759200581.2',
'__jdb': '143920055.1.1759198339988240393195|2.1759200581',
'sdtoken': 'AAbEsBpEIOVjqTAKCQtvQu179H4TgU1sQiwY_jgIlLkfzub2EneuGP2sLpz488hVNgAiBKppDnR-9KGdRFMb2fGttHfYI6x6ZfMbttuK65GA90pBEalHNm_-LbXYXth3my7jHZ5D0UqmCIA',
}
headers = {
'accept': 'application/json, text/plain, */*',
'accept-language': 'zh-CN,zh;q=0.9',
'origin': 'https://search.jd.com',
'priority': 'u=1, i',
'referer': 'https://search.jd.com/',
'sec-ch-ua': '"Chromium";v="140", "Not=A?Brand";v="24", "Google Chrome";v="140"',
'sec-ch-ua-mobile': '?0',
'sec-ch-ua-platform': '"Windows"',
'sec-fetch-dest': 'empty',
'sec-fetch-mode': 'cors',
'sec-fetch-site': 'same-site',
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36',
'x-referer-page': 'https://search.jd.com/Search',
'x-rp-client': 'h5_1.0.0',
# 'cookie': '__jdv=143920055|direct|-|none|-|1759198339989; __jdu=1759198339988240393195; 3AB9D23F7A4B3CSS=jdd0336CXCJW76HVLG5NN3HX7RRXSOAKV7PM4O2R2CEWREEYEERJ3MGNRRZZL6AKZQDEG6JSLQTBE5CCGNYDHCKPS4VKR4EAAAAMZTBSF5TAAAAAADED2AUUGHUOS44X; mba_muid=1759198339988240393195; wlfstk_smdl=di09prnlss3k3bnvofmpwmkjo812881l; TrackID=1RVWFVwuwegh6iaq2dUaSUyxtlv2W8ZsWmxP21tBeuzA6JLP01W25RW4ZNKGWhDFKBR_xMLfaTohtTorBRBpdNlxBRXHQ87UD-YA5CjBNN54; thor=2DD9CF08CB217E996764A091B245EB29B43DEECE308DCF74BCBC66126340B7CC57EDD320176A72EA74043C4CDD7F4324D872C1E3813F23CFC94796AE1ABFB72BC0EB76990CBC9F42DD82C52859CD62E4DF2374343ED9606AFB4437FA8D8FACB1753AD87ACE74EDE1B970E782C73FFEFB25EBC28E00E7D303C43E5B5FDC1C3826210EFE4E1F9CC0F684515F5703EB90D7302414E086525715F186A95323BEAE81; light_key=AASBKE7rOxgWQziEhC_QY6yaKYl0-nkwHhCq7N-FMTwrP69jFjUruev7Rsm2eNMxzUBY6VIP; pinId=w8Ih7LOJnxwxhZalSpUGsbV9-x-f3wj7; pin=jd_52b15ac66b44e; unick=%E5%91%86%E5%91%867115; ceshi3.com=203; _tp=oRxO98xexSXw5ZlTXC2fbDrx5URIy94AUL5RT79KEbs%3D; _pst=jd_52b15ac66b44e; __jdc=143920055; areaId=5; shshshfpa=b9a6c825-8808-1785-a164-cdcc6d85da4a-1759198380; shshshfpx=b9a6c825-8808-1785-a164-cdcc6d85da4a-1759198380; ipLoc-djd=1-72-55653-0; shshshfpb=BApXSoJBtm_xAUGY1ek_5MROtpLvWQ2uQBhtYhK9o9xJ1MhEHxo62; 3AB9D23F7A4B3C9B=36CXCJW76HVLG5NN3HX7RRXSOAKV7PM4O2R2CEWREEYEERJ3MGNRRZZL6AKZQDEG6JSLQTBE5CCGNYDHCKPS4VKR4E; flash=3_6gpgATZhoG6k8czgYn2HnA7xPO3mh2cXAmEzCQ0z422kecKRpx89Biu_-2dtCY3V_jARZ4wIxT3UKzcCONXY1gr97YLyPbCHYjhWNhvMGQGBq_IB6Udnm-kw105qUMUkmMKnu5lY-1pCuNNxyfD0jxFU2_-xm1QimzdddFQTJfmsemqUvJBa3V**; __jda=143920055.1759198339988240393195.1759198340.1759198340.1759200581.2; __jdb=143920055.1.1759198339988240393195|2.1759200581; sdtoken=AAbEsBpEIOVjqTAKCQtvQu179H4TgU1sQiwY_jgIlLkfzub2EneuGP2sLpz488hVNgAiBKppDnR-9KGdRFMb2fGttHfYI6x6ZfMbttuK65GA90pBEalHNm_-LbXYXth3my7jHZ5D0UqmCIA',
}
# 访问的接口
url = "https://api.m.jd.com/api"
# body数据 就是一个json数据 有第几页 在这里修改page
body = "\\{\"page\":1,\"area\":\"7_527_35108_56983\",\"pvid\":\"c7edfed410bb4172956eb62ce0ad72f4\"\\}"
# 去我们通过express开放的接口去获取h5st
response = requests.post("localhost:3000/api/data", json={
"params": {"appid": "search-pc-java",
"functionId": "pc_search_adv_Search",
"body": sha256_hash(body) # 把这个body做了一下sha256
}
})
param = response.json()["result"]
print("param:::",len(param['h5st']))
params = {
"appid": "search-pc-java",
"t": [
str(param["t"]),
str(int(time.time() * 1000))
],
"client": "pc",
"clientVersion": "1.0.0",
"uuid": "1745841960334352365307",
"keyword": "手机", # 在这里修改类型
"functionId": "pc_search_getShopAndWare",
"body": body,
"x-api-eid-token": "jdd03QJJ7DOUYP7T5O2IKSRFQANXZJYHALCU3ECRYXYVSULUXN7DODVWDAGUVYK2WLOTISQ3XQ7U7G5PP57CC2QFRLQFA5AAAAAMYYUR7TVIAAAAACORWVTOFTVSAUQX",
"h5st": param["h5st"]
}
response = requests.get(url, headers=headers, cookies=cookies, params=params, impersonate='chrome101')
print(response.text)
print(response)
1.5.2、express文件
// 引入 express
const express = require('express');
const app = express();
const fs = require('fs');
const { get_h5st } = require('./04 h5st');
ljc_log = console.log;
// 支持 JSON 请求体解析
app.use(express.json());
// 一个 GET 接口
app.get('/api/hello', (req, res) => {
res.json({ message: 'Hello from Node.js backend!' });
});
// 一个 POST 接口
app.post('/api/data', (req, res) => {
// eval(fs.readFileSync('myserver\\jingdong\\5.2.0\\env.js', 'utf8'))
ljc_log('接收到数据:', req.body["params"]);
res.json({ status: 'success', result: get_h5st(req.body["params"]) });
});
// 监听端口
const PORT = 3000;
app.listen(PORT, () => {
console.log(`✅ Server is running at http://localhost:${PORT}`);
});
/*
* express 就是编程里面的web框架 就是对外开一个接口
* npm install express 需要安装一下这个
* */
1.5.3、JS文件

以上是:一、补环境2个工具 在任何的案例中都可以拿过去用

以上是:三、源码

cryptoJs = require("crypto-js")
// 一、补环境2个工具 在任何的案例中都可以拿过去用
// (1)监控函数
// ***************************第一个补环境工具**************************
// (2)函数构造器
/**
* 创建具有特定属性和方法的构造函数
* 该函数主要用于浏览器环境模拟,可以创建具有严格访问控制、继承支持和原型方法的构造函数
* 通过外部数据存储和Symbol键实现实例数据隔离,提高安全性和防检测能力
*
* @param {string} constructorName - 构造函数的名称,将作为全局变量挂载到window对象上
* @param {boolean} enableStrictMode - 是否启用严格模式验证,启用后需要特定令牌才能调用构造函数
* @param {Array} [propertiesList=[]] - 属性定义列表,支持简单字符串属性或自定义属性描述符
* - 简单属性:字符串形式,如 "name"
* - 自定义属性:数组形式,如 ["all", {get: function() {...}}]
* @param {Object} [prototypeMethods={}] - 要添加到原型上的方法
* @param {string} [parentConstructorName=null] - 父构造函数的名称,用于实现继承
* @returns {Function} 返回新创建的构造函数,同时会挂载到window对象上
*
* @example
* // 创建简单的Person构造函数
* createConstructor("Person", true, ["name", "age"], {
* greet: function() { return `Hello, ${this.name}`; }
* });
*
* function Person(){
* this.name = name
* this.age = age
* greed:function(){
* return `Hello, ${this.name}`;
* }
* }
*
*
* @example
* // 创建继承自Animal的Dog构造函数
* createConstructor("Dog", true, ["breed"], {
* bark: function() { return "Woof!"; }
* }, "Animal");
*
* function Dog(){ 这个Dog继承Animal
* this.name = name
* this.age = age
* bark: function() {
* return "Woof!";
* }
* }
*/
function createConstructor(constructorName, enableStrictMode, propertiesList = [], prototypeMethods = {}, parentConstructorName = null) {
// 使用对象存储所有实例的数据,实现数据隔离
// 每个实例通过Symbol键关联到其数据,提高安全性
const instancesData = {};
// 创建构造函数
const Constructor = function (element, propertySetter, validationToken) {
// 验证构造函数调用合法性
if (enableStrictMode && !(validationToken && validationToken === "ljc")) {
throw new Error("Illegal constructor");
}
// 调用父构造函数(如果存在)
if (parentConstructorName && window[parentConstructorName]) {
window[parentConstructorName].call(this, element, null, "ljc");
}
// 设置对象属性
if (propertySetter && typeof propertySetter === "function") {
propertySetter(this);
}
// 初始化元素属性
const instanceProperties = element && typeof element === "object" ? {...element} : {};
// 创建唯一标识符
this._element = Symbol('_element');
instancesData[this._element] = instanceProperties;
};
// 设置继承关系
if (parentConstructorName && window[parentConstructorName]) {
Constructor.prototype = Object.create(window[parentConstructorName].prototype);
Constructor.prototype.constructor = Constructor;
Constructor.__proto__ = window[parentConstructorName];
}
// 设置toStringTag
Object.defineProperty(Constructor.prototype, Symbol.toStringTag, {
value: constructorName,
writable: false,
enumerable: false,
configurable: true
});
// 添加原型方法
Object.keys(prototypeMethods).forEach(methodName => {
Constructor.prototype[methodName] = prototypeMethods[methodName];
});
// 将构造函数挂载到全局对象
window[constructorName] = Constructor;
return Constructor;
}
// 二、补环境
// (1) window对象处理
global_val = globalThis // globalThis 其实就是global 就是node中的顶级变量
// 给global加一个window属性哈 给global全局配一个window属性 给global顶级变量挂载一个全局变量window
Object.defineProperty(global_val, "window", {
get: function () {
return global_val
},
set: function set_window(val) {
global_val = val
},
enumerable: true,
configurable: true,
})
// 无论代码调取那个都是指向这个window 做一个全局处理
window.top = window.self = window.window = window
// (2)补原型链环境
/* 补大Window
* 小window和大Window是两个东西,小window类似于是大Window的实例化对象,大Window是类的概念
* */
// 创建Window空间 createConstructor 一般处理的都是大Window这样的类对象,小window一半都是我们自己构建
createConstructor("Window", true, [], {})
console.log(Window)
// 小window需要和大Window建立父子关系
window.__proto__ = Window.prototype
// 给小window补toStringTag方法 因为小window是单独处理的,所以需要单独把这些加进来
Object.defineProperty(window, Symbol.toStringTag, {
value: "Window", // '[object Window]' 因为是这个字符串所以需要返回Window
writable: false,
enumerable: false,
configurable: true
});
window.document = {}
/* 补Element
* Element --》 Node --》 EventTarget --》 Object
* 因为Element的父类是Node,Node的父类是EventTarget,EventTarget的父类Object,而且在createConstructor方法中,每次在最后面也只能添加一层的父类
* 所以我们给Element设置完父类Node以后,还需要给Node以同样的方式设置父类EventTarget 连续三个createConstructor,这才完成了一个原型串
* */
createConstructor("EventTarget", true, [], {}) // EventTarget 后面就没爹了,没爹不用写,默认是Object
createConstructor("Node", true, ["parentNode", "childNodes"], {}, "EventTarget")
createConstructor("Element", true, ["childElementCount", "innerHTML"], {}, "Node")
// 补document的爹和爷 document --》 HTMLDocument --》 Document --》 node 经过查看链,这些方法都在Document中,所以就补在Document类中
createConstructor("Document", true, [], {
createElement: function (tagName) {
// 打印一下tagName 看一下创建的是那个标签
console.log("Document prototype createElement ", tagName)
if (tagName === "script") {
// 因为script后面也有可能调用了别的,所以我们在这里也需要进行监控
script = watch(new HTMLScriptElement({
parentNode: watch(new HTMLHeadElement(null, null, "ljc"), "script的parentNode")
}, null, "ljc"), "script对象")
return script
}
// canvas和上面的script就一样了
if (tagName === "canvas") {
return {}
}
},
createEvent: function () {
},
querySelector: function () {
},
getElementsByTagName: function () {
},
}, "Node")
createConstructor("HTMLDocument", true, [], {}, "Document")
// 补document.all 的爹直接就是Object
createConstructor("HTMLAllCollection", true, [], {})
// 补document.documentElement HTMLHtmlElement --》 HTMLElement --》 Element --》 Node --》 EventTarget --》 Object
createConstructor("HTMLElement", true, [], {}, "Element")
createConstructor("HTMLHtmlElement", true, [], {}, "HTMLElement")
// 补document.body HTMLBodyElement --> HTMLElement --> Element --> Node --> EventTarget --> Object
createConstructor("HTMLBodyElement", true, [], {}, "HTMLElement")
//补script
createConstructor("HTMLScriptElement", true, [], {}, "HTMLElement")
//补parentNode
createConstructor("HTMLHeadElement", true, [], {}, "HTMLElement")
// (3)添加监控
// 监控document
// window.document = watch({},"document") 给document对象先置空{},然后在监控起来,监控的名字是"document",在返回window.document
//如果案例不检测原型链,我们就可以使用基础补环境直接documenti = {} 按照上面的先置空,然后在监控,但是本案例对原型链进行了检测,
// 所以我们需要先通过dir(document)先找到它爹它爷,然后补HTMLDocument --》Document --》 node这样的原型链出来, 最后再用创建的爹HTMLDocument new一个document对象出来
window.document = watch(new HTMLDocument({
// 这里是document实例化的时候传入的一些属性
// all:new HTMLAllCollection(null,null,"ljc") 其实这样就实例化一个对象就可以了,但是还有一种可能是document.all.xxx 所以我们也需要在实例化的同时也监控一下
// 所以我们就watch(new HTMLAllCollection(null,null,"ljc"),谁调用的呀)document.all.xxx,也就被监控了
all: watch(new HTMLAllCollection(null, null, "ljc"), "document.all"),
//按照上面的方式补全documentElement
documentElement: watch(new HTMLHtmlElement(null, null, "ljc"), "document.documentElement"),
//在补一个cookie属性
cookie: '__jdv=143920055|direct|-|none|-|1759198339989; __jdu=1759198339988240393195; 3AB9D23F7A4B3CSS=jdd0336CXCJW76HVLG5NN3HX7RRXSOAKV7PM4O2R2CEWREEYEERJ3MGNRRZZL6AKZQDEG6JSLQTBE5CCGNYDHCKPS4VKR4EAAAAMZTBSF5TAAAAAADED2AUUGHUOS44X; _gia_d=1; xapieid=jdd0336CXCJW76HVLG5NN3HX7RRXSOAKV7PM4O2R2CEWREEYEERJ3MGNRRZZL6AKZQDEG6JSLQTBE5CCGNYDHCKPS4VKR4EAAAAMZTBSF5TAAAAAADED2AUUGHUOS44X; mba_muid=1759198339988240393195; mba_sid=17591983546191142823231.1; wlfstk_smdl=di09prnlss3k3bnvofmpwmkjo812881l; TrackID=1RVWFVwuwegh6iaq2dUaSUyxtlv2W8ZsWmxP21tBeuzA6JLP01W25RW4ZNKGWhDFKBR_xMLfaTohtTorBRBpdNlxBRXHQ87UD-YA5CjBNN54; pinId=w8Ih7LOJnxwxhZalSpUGsbV9-x-f3wj7; pin=jd_52b15ac66b44e; unick=%E5%91%86%E5%91%867115; ceshi3.com=203; _tp=oRxO98xexSXw5ZlTXC2fbDrx5URIy94AUL5RT79KEbs%3D; __jda=143920055.1759198339988240393195.1759198340.1759198340.1759198340.1; __jdb=143920055.4.1759198339988240393195|1.1759198340; __jdc=143920055; o2State=; is_avif=onAVIF; areaId=5; shshshfpa=b9a6c825-8808-1785-a164-cdcc6d85da4a-1759198380; shshshfpx=b9a6c825-8808-1785-a164-cdcc6d85da4a-1759198380; cn=160; ipLoc-djd=1-72-55653-0; shshshfpb=BApXSoJBtm_xAUGY1ek_5MROtpLvWQ2uQBhtYhK9o9xJ1MhEHxo62; sdtoken=AAbEsBpEIOVjqTAKCQtvQu17mfvlXCqDS0kDr1URwxQHljkUa_9A_ZlGxFL5FumtPOEc2qL3IJkeuNb1O6o04CUR4DMk0etLSPh823yKNu4WG_MO2ZAQTa1qerWUv7eZjfgraGRgUhS_8NI; 3AB9D23F7A4B3C9B=36CXCJW76HVLG5NN3HX7RRXSOAKV7PM4O2R2CEWREEYEERJ3MGNRRZZL6AKZQDEG6JSLQTBE5CCGNYDHCKPS4VKR4E',
//补body
body: watch(new HTMLBodyElement(null, null, "ljc"), "document.body"),
}, null, "ljc"), "document")
// 监控window,名字是"window" 重新用window 重新用的名字 = watch(监控对象, 监控的名字)
window = watch(window, "window")
// 三、源码
// ***************************此处补充源码**************************
// 四、测试
// 使用源码进行new,通过实例化创建window.PSign对象
/*window.PSign = new window.ParamsSign({
appId: 'f06cc',
preRequest: false,
onSign: (res) => {
// 签名可用率监控,业务方自行上报
if (res.code != 0) {
try {
window.dra &&
window.dra.sendCustomEvent &&
window.dra.sendCustomEvent({
name: 'main_search',
metrics: {
error_code: '751',
error_type_txt: '接口加密失败onSign非0',
},
context: {
error_code: res.code,
},
})
} catch (error) {
console.log(error)
}
}
},
onRequestTokenRemotely: (res) => {
// 算法接口可用率监控,业务方自行上报
if (res.code != 200) {
try {
window.dra &&
window.dra.sendCustomEvent &&
window.dra.sendCustomEvent({
name: 'main_search',
metrics: {
error_code: '751',
error_type_txt: '接口加密失败onRequestTokenRemotely',
},
context: {
error_msg: res && res.message ? res.message : '接口加密失败',
},
})
} catch (error) {
console.log(error)
}
}
},
})
const paramsH5sign = {
appid: 'search-pc-java',
functionId: "pc_search_adv_Search",
client: 'pc',
clientVersion: '1.0.0',
t: new Date().getTime(),
}
params = {
"ad_ids": "292:6",
"xtest": "new_search",
"ec": "utf-8",
"area": "1",
"page": "2",
"simpleSearch": "0"
}
paramsH5sign.body = cryptoJs.SHA256(JSON.stringify(params))
// 使用实例化创建的对象,替换this
ret = window.PSign._$sdnmd(paramsH5sign);
console.log(":::::", ret)*/
function main(paramsH5sign) {
//取源码
// ***************************此处补充源码**************************
// new 实例化
encrypt = new ParamsSignMain({
appId: "fb5df",
debug: false,
onSign: function (t) {
},
onRequestToken: function (t) {
},
onRequestTokenRemotely: function (t) {
},
bucket: "0.1.8"
})
// 传进来的 paramsH5sign 只在这里进行了取值操作
result = encrypt._$sdnmd({
"appid": paramsH5sign['appid'],//"item-v3",
"functionId": paramsH5sign['functionId'],//"pc_stocks",
"client": "pc",
"clientVersion": "1.0.0",
"t": +new Date(),
"body": paramsH5sign['body']
})
console.log("H5ST:::", result["h5st"].length, result)
document_all = 0
return result
}
// module.exports js的导出,导出那个函数,导出main这个函数,对外叫什么名字,对外叫get_h5st
module.exports = {get_h5st: main}