使用Python模拟腾讯第三方认证-篇2
admin
2023-07-30 20:44:22
0

上篇分析了模拟登陆的流程,以及HTTP请求和回应,下面我们开始编码实现。

编码

准备

安装requests, pip install requests

使用requests请求比使用urllib方便了很多,下面简单封装了一个http请求函数fetch:

\'\'\'
默认为GET,如果赋值data后则为POST方式
\'\'\'
def fetch(self, url, data=None, **kw):
    if data is None:
        func = self.session.get
    else:
        kw[\'data\'] = data
        func = self.session.post
    return func(url, **kw)

使用QQ登录

点击‘QQ登录’按钮后会以GET方式请求http://passport.jikexueyuan.com/connect/qq, 请求代码如下:

self.jkqqurl=\'http://passport.jikexueyuan.com/connect/qq\'

response = self.fetch(self.jkqqurl)
self.state = re.findall(\'&state=(.*?)\\&\', response.url)[0]            #(.*?), 括号为返回匹配值,即=后面的
self.client_id = re.findall(\'&client_id=(.*?)\\&\', response.url)[0]

因为下面向腾讯请求认证需要学院返回的这两个参数:STATE, CLIENTID, 这里记录下来。

请求登录页面

通过上步请求后,虽然学院发起了向腾讯认证服务器的请求,但是因为没有登录会被重新定向至登录授权页面,
这里我们手动请求一次封装在IFrame中的登录页面:

self.xloginurl=\'http://xui.ptlogin2.qq.com/cgi-bin/xlogin\'

g = self.fetch(self.xloginurl, params = {
        \'appid\': self.appid,
        \'style\': 23,
        \'login_text\': \'授权并登录\',
        \'hide_title_bar\': 1,
        \'target\': \'self\',
        \'s_url\': self.loginjumpurl,
        \'pt_3rd_aid\': 101119675,
        \'pt_feedback_link\': self.feedbackurl,
    }).text

这步操作理论上会产生一些登录请求的会话信息,还是尽量模拟浏览器操作,后面再作精简。

登录校验

分析来看,腾讯根据登录请求类型来产生校验码图片或文字型校验码(如果不需要校验码图片),看代码:

self.checkurl2=\'http://check.ptlogin2.qq.com/check\'

g = self.fetch(self.checkurl2, params = {
        \'regmaster\': \'\',
        \'pt_tea\': 1,
        \'pt_vcode\': 1,
        \'uin\': self.user,
        \'appid\': self.appid,
        \'js_ver\': 10120,
        \'js_type\': 0,
        \'login_sig\': self.session.cookies[\'pt_login_sig\'],
        \'u1\': self.loginjumpurl,
    }).text
v = re.findall(\'\\\'(.*?)\\\'\', g)
vcode = v[1]
uin = v[2]

这里pt_login_sig是从session中取得,requests从session中获取参数还是很方便的。

返回值格式:

ptui_checkVC(
    \'0\',        #参数为是否需要校验码
    \'!JCD\',        #系统生成的校验码
    \'\\x00\\x00\\x00\\x00\\x3x\\x2x\\x3x\\x3x\',    #用户ID进行转化后的值
    \'47e6754aafb6138d4cdcdfdd1602bc8df9d29893cc705484d148adb079105ebba462f8cd7c18025441c5309c1b50378059fb5a0fcd18b206\', #ptvfsession,后面登录使用
    \'0\');

登录

登录时判断是否需要校验码,如果需要则下载校验码图片。
开篇说过,腾讯第三方登录认证是不需要校验码的,从上面结果也能看出v[0]一直为0

self.loginurl2=\'http://ptlogin2.qq.com/login\'

if v[0] == \'1\':    # 需要校验码
    vcode = self.getVerifyCode(vcode) #获得校验码
g = self.fetch(self.loginurl2, params = {
    \'u\': self.user,
    \'verifycode\': vcode,
    \'pt_vcode_v1\': 0,
    \'pt_verifysession_v1\': self.session.cookies[\'ptvfsession\'],
    \'p\': self.pwdencode(vcode, uin, self.pwd),
    \'pt_randsalt\': 0,
    \'u1\': self.loginjumpurl,
    \'ptredirect\': 0,
    \'h\': 1,
    \'t\': 1,
    \'g\': 1,
    \'from_ui\': 1,
    \'ptlang\': 2052,
    \'action\': self.action,
    \'js_ver\': 10138,
    \'js_type\': 0,
    \'login_sig\': self.session.cookies[\'pt_login_sig\'],
    \'pt_uistyle\': 33,
    \'aid\': self.appid,
    \'pt_3rd_aid\': 101119675,
}).text

登录处理这块处理的东西较多,分开看:

  • ptvfsession和pt_login_sig都是从cookies中取得
  • 密码加密

    为了防止密码被截取,肯定做足了混淆加密,具体算法可以参考c_login_2.js中的实现,这里参考了qqlib的代码http://www.jianshu.com/p/4217d8f3574b

def fromhex(self, s):
    # Python 3: bytes.fromhex
    return bytes(bytearray.fromhex(s))

#pip install rsa
pubKey=rsa.PublicKey(int(
    \'F20CE00BAE5361F8FA3AE9CEFA495362\'
    \'FF7DA1BA628F64A347F0A8C012BF0B25\'
    \'4A30CD92ABFFE7A6EE0DC424CB6166F8\'
    \'819EFA5BCCB20EDFB4AD02E412CCF579\'
    \'B1CA711D55B8B0B3AEB60153D5E0693A\'
    \'2A86F3167D7847A0CB8B00004716A909\'
    \'5D9BADC977CBB804DBDCBA6029A97108\'
    \'69A453F27DFDDF83C016D928B3CBF4C7\',
    16
), 3)

def pwdencode(self, vcode, uin, pwd):
    # uin is the bytes of QQ number stored in unsigned long (8 bytes)
    salt = uin.replace(r\'\\x\', \'\')
    h1 = hashlib.md5(pwd.encode()).digest()
    s2 = hashlib.md5(h1 + self.fromhex(salt)).hexdigest().upper()
    rsaH1 = binascii.b2a_hex(rsa.encrypt(h1, self.pubKey)).decode()
    rsaH1Len = hex(len(rsaH1) // 2)[2:]
    hexVcode = binascii.b2a_hex(vcode.upper().encode()).decode()
    vcodeLen = hex(len(hexVcode) // 2)[2:]
    l = len(vcodeLen)
    if l < 4:
        vcodeLen = \'0\' * (4 - l) + vcodeLen
    l = len(rsaH1Len)
    if l < 4:
        rsaH1Len = \'0\' * (4 - l) + rsaH1Len
    pwd1 = rsaH1Len + rsaH1 + salt + vcodeLen + hexVcode
    saltPwd = base64.b64encode(
        tea.encrypt(self.fromhex(pwd1), self.fromhex(s2))
    ).decode().replace(\'/\', \'-\').replace(\'+\', \'*\').replace(\'=\', \'_\')
    return saltPwd
  • 获取校验码:
#这里只是给出给参考,从WebQQ登录查询校验码过程获得
self.imgurl=\'http://captcha.qq.com/getimage\'
def getVerifyCode(self, vcode):
    r = self.fetch(self.imgurl, params = {
        \'r\':0,
        \'appid\':self.appid,
        \'uin\':self.user,
        \'vc_type\':vcode,
    })
    tmp = tempfile.mkstemp(suffix = \'.jpg\')
    os.write(tmp[0], r.content)
    os.close(tmp[0])
    os.startfile(tmp[1])
    vcode = input(\'Verify code: \')
    os.remove(tmp[1])
    return vcode

如果登录成功则返回值如下:
ptuiCB(\'0\',\'0\',\'http://openapi.qzone.qq.com/oauth/login_jump\',\'0\',\'登录成功!\', \'XXXX\');

登录跳转

代码很简单:

self.loginjumpurl=\'http://openapi.qzone.qq.com/oauth/login_jump\'
g = self.fetch(self.loginjumpurl).text

这一步会返回这样一个页面:




    
    



这一步,对于模拟情况作用不大,对于浏览器的话会执行上报报告,表格提交等操作,后面会模拟。
重点是JS块中的parent.agree详细操作流程,可以从qlogin_v2.js中分析到。

上传报告

这两个报告没有特别的:

self.appsupporturl=\'http://appsupport.qq.com/cgi-bin/appstage/mstats_report\'
self.reporturl=\'http://cgi.connect.qq.com/report/report_vm\'

g = self.fetch(self.appsupporturl, params = {
    \'report_type\': \'4\',
    \'platform\': 8,
    \'app_id\': \'101119675\',
    \'result\': 0,
    \'act_type\': 2,
    \'uin\': self.user,
    \'login_status\': 2,
    \'via\': 1,
    \'t\': int(time.time()),
    }).text
#正确的话返回{\"ret\":0,\"msg\":\"成功\"}

g = self.fetch(self.reporturl, params = {
        \'tag\': 0,
        \'log\': \'101119675_10613_0\',
        \'t\': int(time.time()),
    }).text

#正确的话返回{\"ec\":0}

获取授权

self.authurl=\'https://graph.qq.com/oauth2.0/authorize\'
#更新请求头,防止被屏蔽
self.session.headers.update({\'User-Agent\': \'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 Safari/537.36 Edge/12.10240\'})

token = self.gettoken()
data = {
    \'response_type\': \'code\',
    \'client_id\': 101119675,
    \'redirect_uri\': \'http://passport.jikexueyuan.com/connect/success?t=qq\',
    \'scope\': \'get_user_info\',
    \'state\': self.state,
    \'src\': \'1\',
    \'update_auth\': \'1\',
    \'openapi\': \'80901010\',
    \'g_tk\': token,
    \'auth_time\': int(time.time()),
    \'ui\': \'556F791F-682E-47B7-BC66-CE179327DB04\',#UID,可以随机生成,这里简单使用了抓包结果
}
response = self.fetch(self.authurl, data = data)

gettoken定义如下:

def gettoken(self):
    str = self.session.cookies[\'skey\']
    hash = 5381
    for i in str:
        t = (hash << 5) + ord(i)
        hash = hash + t
    return hash & 0x7fffffff

如果成功的话,则会跳转至学院主页。
如果失败,需要分析是哪一步错了,主要有:密码,STATE, pt_login_sig. ptvfsession。

试验

self.session.headers.update({\'User-Agent\': \'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 Safari/537.36 Edge/12.10240\'})
g = self.fetch(\'http://www.jikexueyuan.com/course/2185_1.html?ss=1\').text
print g

查看打印信息,看是否已经登录即可。

总结

编码流程基本是针对登录过程抓包的实现,比较原始,并且刚开始写python程序,后面再整理出一个干净的版本。

到现在为止所做的足够对学院进行爬虫分析了,后续更新。

需要注明的是,代码主要参考了qqlib, 作者@JetLu http://www.jianshu.com/users/4d556dbb651e/latest_articles

相关内容

热门资讯

500 行 Python 代码... 语法分析器描述了一个句子的语法结构,用来帮助其他的应用进行推理。自然语言引入了很多意外的歧义,以我们...
定时清理删除C:\Progra... C:\Program Files (x86)下面很多scoped_dir开头的文件夹 写个批处理 定...
65536是2的几次方 计算2... 65536是2的16次方:65536=2⁶ 65536是256的2次方:65536=256 6553...
Mobi、epub格式电子书如... 在wps里全局设置里有一个文件关联,打开,勾选电子书文件选项就可以了。
scoped_dir32_70... 一台虚拟机C盘总是莫名奇妙的空间用完,导致很多软件没法再运行。经过仔细检查发现是C:\Program...
pycparser 是一个用... `pycparser` 是一个用 Python 编写的 C 语言解析器。它可以用来解析 C 代码并构...
小程序支付时提示:appid和... [Q]小程序支付时提示:appid和mch_id不匹配 [A]小程序和微信支付没有进行关联,访问“小...
Prometheus+Graf... 一,Prometheus概述 1,什么是Prometheus?Prometheus是最初在Sound...
python绘图库Matplo... 本文简单介绍了Python绘图库Matplotlib的安装,简介如下: matplotlib是pyt...
微信小程序使用slider实现... 众所周知哈,微信小程序里面的音频播放是没有进度条的,但最近有个项目呢,客户要求音频要有进度条控制,所...