案例:通过协议模拟登录58同城

---
login url : https://passport.58.com/58/login/pc/dologin

观察了一下需要注意的参数有:
- username: 账号
- password: 加密(密码)
- token: 密文
password是密码加密后的定值,token是动态的。
ctrl+F搜索一下token值,找到了返回token的接口
可以观察出获取token的接口中, callback和path参数 跟时间戳有关

点击右侧的 Initiator , 点第一个 r 进去,打上断点,刷新页面

刷新之后可以看到,如下图所示

可以发现此处i中的callback参数是和t的值一样

往上找一找t是如何生成的

var t = e.jsonpCallback || "JsonpCallBack" + (new Date).getTime() + Math.floor(1e3 * Math.random())
既 t = "JsonpCallBack" + 时间戳 + 随机数
这里可以把js复制下来用execjs生成,也可以用python来模拟:
import math,time,random
print("JsonpCallBack" + str(int(time.time()*1000)) + str(math.floor(1000*random.random())))
经过观察,path参数 "固定的url" + "13位时间戳"
接着可以来用代码构造下请求,获取token值
import math,random,time
import requests
pathtime = str(int(time.time() * 1000))
jsoncallback = "JsonpCallBack" + str(int(time.time() * 1000)) + str(math.floor(1000.0 * random.random()))
url = "https://passport.58.com/58/login/init?source=58-homepage-pc&path=https%253A%252F%252Fbj.58.com%252F%253Fpts%253D{}&psdk-d=jsdk&psdk-v=1.0.6&callback=JsonpCallBack{}".format(pathtime,jsoncallback)
headers = {
'referer': 'https://passport.58.com/login?path=https%3A%2F%2Fmy.58.com%2F',
'sec-fetch-dest': 'script',
'sec-fetch-mode': 'no-cors',
'sec-fetch-site': 'same-origin',
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.75 Safari/537.36',
}
print(requests.get(url,headers=headers).text)
打印出的结果中可以提取到token
JsonpCallBackJsonpCallBack1605081373854724({"code":0,"data":{"complainSwitch":true,"rememberSwitch":true,"token":"CJQM-Txgu-IiNWn-4F5Td-kJAgEPcHtd","action":"0"},"msg":"成功"})
其实拿到token后就可以完成模拟登录了,感兴趣的话可以一起再找一下登录密码是如何进行加密的。
还是先搜索参数位置,找到一个最像的

可以通过关键词password 搜到 n.encrypt,然后找到了这里,断点调试下

可以发现,e.password 就是我们输入的密码,n.encrypt 就是加密后的结果

所以点进去看一下

在该位置断点
e点进去可以发现

在生成i,o后,经过处理传递到 encryptString

encodeURIComponent(): 这里是 Native code 函数: 把字符串作为 URI 组件进行编码,(正常的密码可以不管)
rsaExponset 和 rsaModulus 这两个值直接复制出来,
r.rsaExponent = "010001"
r.rsaModulus = "008baf此处省略N多字40db74cb69f"

点击跳转到 encryptString 方法查看:
t和s已经有了,o可以在上面找到,时间戳+i,i=定值-时间戳

可是加密好像跟密码没有什么关系呀,回过头看了一下,才发现 o 在外层是和密码拼接了一下。

接下来组合上面的js,先把encryptString全部复制到一个js文件中,
然后定义一个方法传入参数来调用。
为了省空间,先把前面的改动贴出来,最后再放完整的代码。

function encryptString(pwd) {
var ii = 1411093327735 - (new Date).getTime();
var o = (new Date).getTime() + ii;
var i = o + pwd;
var r = RSAUtils.getKeyPair("010001", "", "008baf14121377fc76eaf7794b8a8af17085628c3590df47e6534574efcfd81ef8635fcdc67d141c15f51649a89533df0db839331e30b8f8e4440ebf7ccbcc494f4ba18e9f492534b8aafc1b1057429ac851d3d9eb66e86fce1b04527c7b95a2431b07ea277cde2365876e2733325df04389a9d891c5d36b7bc752140db74cb69f");
return RSAUtils.encryptedString(r, i)
}
!function(e) {
void 0 === e.RSAUtils && (e.RSAUtils = {});
//此处省略N行代码
//此处省略N行代码
RSAUtils.setMaxDigits(130)
}(window);
把js复制到控制台测试一下,返回的结果和最初看到的一样,说明成功了

下面可以构筑完整的登录代码了
目前这里文章发布后不能修改,还是贴csdn的链接,后边有改动方便更新
您好,本帖含有特定内容,请回复后再查看。
输出的结果:

返回的response-header中,set-Cookie就是登录成功后服务端发放的cookie。
如果返回中没set-cookie的话,可能是你的账号风险程度较高,登录时需要验证码。
如果是图文验证码,可以想办法识别后,写入data的参数中,重新请求,
如果是向平台发短信验证的话,推荐换号吧。