文章来源: 52pojie
本人是作者
心里测评网站登录接口分析
前言
最近我校突然让我们制作心理测评网站,然后登录中发现有滑块验证码等操作(之前都是直接登录无需验证码,现在界面变的很整洁以及引入了验证码登录) 本人目前初二(八年级)第一次写文章,所以文章可能会有些地方写的不清楚,请谅解! 网站:aHR0cHM6Ly96eXpmeC5wc3l5dW4uY29tLw==
开始分析
验证码获取
验证流程(失败处理): base64图像示例: originalImageBase64:
jigsawImageBase64:
可以看到是滑块验证码 这个是比较好解的(ddddocr可以解决) 重点是在请求 接下来是验证成功的请求流程:
解析: 获取验证码:
Get https://zyzfx.psyyun.com/code?userName=<UserName>
// UserName为用户名
Headers:
Authorization: Basic xxx //Auth必备条件
TENANT-ID: 440 // 租户ID(相当于学校ID) Authorization固定的值:
TENANT-ID生成方法:
Get https://zyzfx.psyyun.com/admin/tenant/detailByCode?code=<your school code>
// 其中code为学校的代码(网址前缀)Result:
很好,关键的一些请求头都获取到了 接下来是编写Python代码(获取验证码):
import requests
# Disable SSL Verification
requests.packages.urllib3.disable_warnings()
# Init
session = requests.Session()
session.verify = False
session.headers = {
"Authorization": "Basic xxx", // 获取到的Authorization(可直接写进里面)
"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 Edg/140.0.0.0",
"Accept": "application/json, text/plain, */*",
"Content-Type": "application/x-www-form-urlencoded;charset=UTF-8", // 默认请求方式
"TENANT-ID": "440", // 租户ID
}
UserName = "xxx" // 用户名
captcha = session.get(f"https://zyzfx.psyyun.com/code?userName={UserName}&captchaType=blockPuzzle").json() // 获取验证码
imageOriginalBase64 = captcha["data"]["repData"]["originalImageBase64"] // 背景图
imageOriginal = base64.b64decode(imageOriginalBase64)
imagePuzzleBase64 = captcha["data"]["repData"]["jigsawImageBase64"] // 拼图
imagePuzzle = base64.b64decode(imagePuzzleBase64)
token = captcha["data"]["repData"]["token"] // token
secretKey = captcha["data"]["repData"]["secretKey"] // 安全密钥验证码验证流程
先分析请求
Post请求,内容为空 Query String: userName: 1 // 用户名 captchaType: blockPuzzle // 验证码类型:滑块验证码 pointJson: c+NDG2sxh8ntCz9nlBvmf3/0T4Z/I1PwkvcC9D2KBlg= // 加密内容 secretKey:aGvrWRMPxKoeuGMe token: c62b362137b542fcab00ff073a1932a1 // code中获取到的token
js分析:
解析(pointJson) 使用AES中的ECB模式加密,填充方式为Pkcs7方式,secretKey为加密密钥 在o.a函数中,t为key(获取arguments,获取第二项作为key密钥(没有默认使用密钥:XwKsGlMcdPMEhR1B))
返回内容分析: 失败:
{
"code": 0,
"msg": "成功",
"data": {
"repCode": "6111",
"repMsg": "验证失败",
"repData": null,
"success": false // 验证失败的判断
},
"success": true
}成功:
{
"code": 0,
"msg": "成功",
"data": {
"repCode": "0000",
"repMsg": null,
"repData": {
"captchaId": null,
"projectCode": null,
"captchaType": "clickWord",
"captchaOriginalPath": null,
"captchaFontType": null,
"captchaFontSize": null,
"secretKey": null,
"originalImageBase64": null,
"point": null,
"jigsawImageBase64": null,
"wordList": null,
"pointList": null,
"pointJson": "c+NDG2sxh8ntCz9nlBvmf3/0T4Z/I1PwkvcC9D2KBlg=", // 请求里面的pointJson
"token": "c62b362137b542fcab00ff073a1932a1", // token值
"result": true,
"captchaVerification": null,
"clientUid": null,
"ts": null,
"browserInfo": null
},
"success": true // 验证成功的判断
},
"success": true
}这个请求更像是在验证是否正确滑到指定位置(无其他返回参数)
登录部分
Query String: randomStr: blockPuzzle // 默认滑块验证码 code: grILnYI7HmW41fTsrP7O/WrHm3qTRbHLt0Rq1KVrvLzdfKCSGGKWwGLnuGB6OHfqN5/1jFY457Zrz+LFL0MGG3RE5ka9Y04xQKpe06cmkOs= // 特殊加密的code,其里面包含了获取验证码的token与PointJson的内容 grant_type: password // 粗略推测应该是使用密码类型登录 username: 1 //用户名
POST内容(表单数据): username: 1 // 用户名 password: Hgn8K/2pda3c5rEnoZIcpA== // 特殊加密的密码
Headers: isToken: false // 默认为false TENANT-ID: 440 // 前文提到的学校ID Authorization: Bearer xxx // 前文提到的Authorization验证部分 Content-Type: application/x-www-form-urlencoded;charset=utf-8 // 默认请求内容的方式:form格式
js代码分析: 先看到key值的部分:
var i = "r"
, r = "u"
, a = "i"
, o = "g"
, c = "e";
function s() {
return "".concat(i).concat(r).concat(a).concat(o).concat(c).concat(i).concat(r).concat(a).concat(o).concat(c).concat(i).concat(r).concat(a).concat(o).concat(c).concat(c)
// 使用拼接的方式拼凑出key,即为ruigeruigeruigee
}通过这段代码可得知密码使用的是AES CBC的模式加密(nopadding)且key和iv均为固定密钥:ruigeruigeruigee
接下来是Code部分 我们回到刚刚验证验证码验证成功后的部分 captchaVerification值是code的证据:
总结:password使用AES CBC nopadding加密,其iv和key均为ruigeruigeruigee code为AES ECB Pkcs7加密,密钥为secretKey,内容为:
<token>---<pointJson原文>俩个加密的解决了,接下来是编写python代码逻辑:
def Login(UserName,Password):
while True:
# Get Captcha
session.headers.update({"Content-Type": "application/x-www-form-urlencoded;charset=UTF-8"})
session.headers.update({"Authorization": "Basic xxx"})
captcha = session.get(f"https://zyzfx.psyyun.com/code?userName={UserName}&captchaType=blockPuzzle").json()
imageOriginalBase64 = captcha["data"]["repData"]["originalImageBase64"]
imageOriginal = base64.b64decode(imageOriginalBase64)
imagePuzzleBase64 = captcha["data"]["repData"]["jigsawImageBase64"]
imagePuzzle = base64.b64decode(imagePuzzleBase64)
token = captcha["data"]["repData"]["token"]
secretKey = captcha["data"]["repData"]["secretKey"]
# DDDOCR
res = ocr.slide_match(imagePuzzle, imageOriginal, simple_target=True)
# Encrypt pointJson
# AES ECB Encrypt
originalJsonData = "{" + f"\"x\":{res['target'][0]}.27272727272727,\"y\":5" + "}"
cipher = AES.new(secretKey.encode(), AES.MODE_ECB)
encrypted_data = cipher.encrypt(pad(originalJsonData.encode(), AES.block_size))
encrypted_code = cipher.encrypt(pad((token + "---" + originalJsonData).encode(), AES.block_size))
encryptedCodeBase64 = base64.b64encode(encrypted_code).decode().replace(" ","+")
encrypted = base64.b64encode(encrypted_data).decode()
encrypted = encrypted.replace(" ", "+")
# Submit Captcha
buildPostJson = {
"userName": UserName,
"captchaType": "blockPuzzle",
"pointJson": encrypted,
"token": token
}
#Json To QueryString
import urllib.parse
queryString = ""
for key, value in buildPostJson.items():
# 对查询参数进行URL编码以避免特殊字符问题
encoded_value = urllib.parse.quote(str(value), safe='')
queryString += f"{key}={encoded_value}&"
queryString = queryString[:-1]
captchaResult = session.post(f"https://zyzfx.psyyun.com/code/check?{queryString}").json()
if captchaResult["data"]["success"] == True:
# # AES CBC Encrypt Password
encrypted_password_b64 = aes_cbc_zero_padding_encrypt(Password, "ruigeruigeruigee")
# Login
# 将URL参数转换为JSON格式再编码为查询字符串
params = {
"randomStr": "blockPuzzle",
"code": encryptedCodeBase64,
"grant_type": "password",
"username": UserName
}
# 手动构建查询字符串,确保特殊字符被正确编码
query_string = "&".join([f"{k}={urllib.parse.quote(str(v), safe='')}" for k, v in params.items()])
url = f"https://zyzfx.psyyun.com/auth/oauth/token?{query_string}"
session.headers.update({"Content-Type":"application/x-www-form-urlencoded","ignore":"1","origin":"https://zyzfx.psyyun.com"})
response = session.post(url, data={"username":UserName,"password": encrypted_password_b64})
loginResult = response.json()
print(f"登录成功,用户名:{loginResult['user_info']['username']}")
session.headers.update({"Authorization":f"Bearer {response.json()['access_token']}"})
break返回内容: 登录失败:
登录成功:
其中需要添加请求头Authorization: Bearer access_token内容 添加这个请求头后即可请求接下来的如获取心理测评表等API操作
声明
本项目仅用于学习交流,请勿非法使用。