前言
现如今越来越多的网站采用QQ或微信作为用户的一种快速登陆方式。随着互联网技术的发展,对自动化登陆的要求也随之产生,那么本文就由QQ出发,深入分析下QQ的授权流程,并如何实现自动化登陆。关键代码不分享,仅供参考学习。
首先简单看看QQ第三方网站授权登陆的流程图
简单原理
接触过自动登陆QQ的朋友都知道,最难的就是过验证码,可以参考目前网上例如使用python或者接码平台等方式,但本文的简化这些步骤,服务器的IP多登陆几次成为常用IP后登陆就不需要验证。当然,本文的重点不是QQ登陆,而是如何授权第三方网站。
1、获取需授权的QQ账号信息
使用Fiddler抓包分析登陆流程,重点要解决p参数的加密过程。按F12调试发现以下函数
可看出共使用了TEA、RSA、BASE64一共3种方式混合加密,并且这3种加密函数全由原生JS实现,以上函数使用PHP重写后:
//16进制字符串转字符串
public function hexToStr($hex){
$str="";
$strlen=strlen($hex);
for($i=0;$i<$strlen;$i+=2)
$str.=chr(hexdec($hex[$i].$hex[$i+1]));
return $str;
}
//QQ账号转16进制字符串
public function QQToHex($str){
$str=dechex($str);
for($i = strlen($str);$i < 16; $i ++){
$str = "0".$str;
}
return $str;
}
//QQ登录验证加密函数参数:QQ密码 账号 验证码 是否加密
public function QQLoginEncrypt($p,$u,$c,$md5="") {
$hexu= $this->QQToHex($u);// 字符串转hex加密
$md5p= md5($p);
$clen= "000".strlen($c);
$binc= bin2hex(strtoupper($c));
$key = strtoupper(md5($this->hexToStr($md5p.$hexu)));// MD5加密
$data= strtoupper($md5p).$hexu.$clen.$binc;
$EnStr = TEAandRSA($key,$data);//TEA加密 与 RSA加密
$EnP=base64_encode($this->hexToStr($EnStr)); //BASE64加密
$EnP=str_replace('/','-',$EnP);
$EnP=str_replace('+','*',$EnP);
$EnP=str_replace('=','_',$EnP);
return $EnP;
}
其中TEA和RSA的加密因为嫌麻烦就直接用PHP运行js代码了(需要安装v8js扩展)。以下函数的JS代码也是在c_login_2.js中提取的。
//判断QQ登录方式
public function CheckQQLoginType(){
$header=array(
"Host: ssl.ptlogin2.qq.com",
"User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:76.0) Gecko/20100101 Firefox/76.0",
"Accept: */*",
"Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2",
"Accept-Encoding: gzip, deflate, br",
"Connection: keep-alive",
"Referer: https://xui.ptlogin2.qq.com/cgi-bin/xlogin?appid=716027609&daid=383&style=33&login_text=%E6%8E%88%E6%9D%83%E5%B9%B6%E7%99%BB%E5%BD%95&hide_title_bar=1&hide_border=1&target=self&s_url=https%3A%2F%2Fgraph.qq.com%2Foauth2.0%2Flogin_jump&pt_3rd_aid=101470313&pt_feedback_link=https%3A%2F%2Fsupport.qq.com%2Fproducts%2F77942%3FcustomInfo%3D.appid101470313",
"Cookie: "
);
$url="https://ssl.ptlogin2.qq.com/check?regmaster=&pt_tea=2&pt_vcode=1&uin=".$this->qqnum."&appid=716027609&js_ver=20032614&js_type=1&login_sig=&u1=https%3A%2F%2Fgraph.qq.com%2Foauth2.0%2Flogin_jump&r=0.101003".time()."&pt_uistyle=40";
$body = $this->curl($url,"","",$header,0);
//ptui_checkVC('0','!FKE','\x00\x00\x00\x00\x2e\x35\x91\x80','258ae60419e861ea579886ca18f76983b2e77ddf7e870e93b0bd70ab14a6e7637752aafb1de9925d594a7ef70eb3ae2e4e89fda3e181cd63','2', 'uIYUkvs2dy7*m*KdoG6jZu7a6lhs8crUEb8shkSpPT097PMKmhLKdlp-tx4opH1-')
$data = str_replace("'","",$this->pregjson($body,"ptui_checkVC"));
$data = str_replace(" ","",$data);
$data = explode(",",$data);
return $data;
}
//登录QQ账号
public function LoginQQAccount($data){
$header=array(
"Host: ssl.ptlogin2.qq.com",
"User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:76.0) Gecko/20100101 Firefox/76.0",
"Accept: */*",
"Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2",
"Accept-Encoding: gzip, deflate, br",
"Connection: keep-alive",
"Referer: https://xui.ptlogin2.qq.com/cgi-bin/xlogin?appid=716027609&daid=383&style=33&login_text=%E6%8E%88%E6%9D%83%E5%B9%B6%E7%99%BB%E5%BD%95&hide_title_bar=1&hide_border=1&target=self&s_url=https%3A%2F%2Fgraph.qq.com%2Foauth2.0%2Flogin_jump&pt_3rd_aid=101470313&pt_feedback_link=https%3A%2F%2Fsupport.qq.com%2Fproducts%2F77942%3FcustomInfo%3D.appid101470313",
"Cookie: ".$data[8]."; confirmuin=".$data[0]."; ptdrvs=".$data[5]."; ptvfsession=".$data[3]."; ptui_loginuin=".$data[6]
);
$url="https://ssl.ptlogin2.qq.com/login?u=".$data[6]."&verifycode=".$data[1]."&pt_vcode_v1=0&pt_verifysession_v1=".$data[3]."&p=".$data[7]."&pt_randsalt=".$data[4]."&u1=https%3A%2F%2Fgraph.qq.com%2Foauth2.0%2Flogin_jump&ptredirect=0&h=1&t=1&g=1&from_ui=1&ptlang=2052&action=2-0-1590368126499&js_ver=20032614&js_type=1&login_sig=&pt_uistyle=40&aid=716027609&daid=383&pt_3rd_aid=101470313&ptdrvs=".$data[5]."&";
$body = $this->curl($url,"","",$header,1);
//ptuiCB('0','0','https://ssl.ptlogin2.graph.qq.com/check_sig?pttype=1&uin=775262592&service=login&nodirect=0&ptsigx=9a2a261166dec9d5feb5413d69910a3d7d2b7dd7de9dce096515c193aa68f5bbaef54e72992f558370ade62dbf7db87129e8ce91cda5fd9d7ec34b3b4a4a4a59&s_url=https%3A%2F%2Fgraph.qq.com%2Foauth2.0%2Flogin_jump&f_url=&ptlang=2052&ptredirect=100&aid=716027609&daid=383&j_later=0&low_login_hour=0®master=0&pt_login_type=1&pt_aid=0&pt_aaid=0&pt_light=0&pt_3rd_aid=101470313','0','登录成功!', '皮皮华')
// return $body;
$data = str_replace("'","",$this->pregjson($body,"ptuiCB"));
$data = str_replace(" ","",$data);
$data = explode(",",$data);
return $data;
}
//获取QQ登录成功后的p_skey
public function QQLoginGetPskey($url){
$json=$this->curl($url,"","",array(),1);
list($header, $body) = explode("\r\n\r\n", $json,2);
preg_match('/Set-Cookie: p_skey=(.*);(.*)graph.qq.com/iU',$header,$str); //正则匹配
$data = "pt_login_type=1; p_skey=".$str[1];
$this->g_tk=$this->getGTK($str[1]);
if(strlen($str[1]) < 1){
$this->code= 202;
$this->msg="获取登录p_skey的Cookie值失败";
return $json;
}
preg_match('/Set-Cookie: p_uin=(.*);(.*)graph.qq.com/iU',$header,$str); //正则匹配
$data= $data . "; p_uin=" .$str[1];
if(strlen($str[1]) < 1){
$this->code= 202;
$this->msg="获取登录p_uin的Cookie值失败";
return $json;
}
preg_match('/Set-Cookie: pt4_token=(.*);(.*)graph.qq.com/iU',$header,$str); //正则匹配
$data= $data . "; pt4_token=" .$str[1];
if(strlen($str[1]) < 1){
$this->code= 202;
$this->msg="获取取登录pt4_token的Cookie值失败";
return $json;
}
$this->code= 200;
$this->msg="获取登录Cookie值成功";
$this->cookie = $data;
file_put_contents("authqq.json",json_encode($data));
return $json;
}
//重新获取QQ授权Cookie
public function GetAuthCookie(){
$data = $this->CheckQQLoginType();
if($data[0] == "0"){//免密登录
$data[6] = $this->qqnum;
$data[7] = $this->QQLoginEncrypt($this->qqpwd,$this->qqnum,$data[1]);//P参数加密
$logindata = $this->LoginQQAccount($data);
if($logindata[4]=="登录成功!"){
$this->QQLoginGetPskey($logindata[2]);
}
}
return $data;
}
2、怎样授权第三方网站并拿到第三方网站的cookie
同样使用Fiddler抓包分析登陆流程,整个过程分3步走。
//获取第三方网站Cookie
public function GetSiteCookie($sitename){
$state= "";
if(strlen($this->site[$sitename]["loginurl"]) > 1){
$state = $this->GetState($sitename);//获取state
}
$url = $this->authorize($sitename,$state);//授权第三方网页
if($this->code == 200){
return $this->GetUrlCookie($sitename,$url);//获取回调网页Cookie
}
}
1)获取state
某些网站在接入Oauth2.0时并没有做state验证,所以也不需要此步骤。以下以网易云音乐为例。
"pph_music163" => array(
"name" => "网易云音乐",//网站名称
"loginurl" => "https://music.163.com/api/sns/authorize?snsType=5&clientType=web2&callbackType=Login&forcelogin=true",//State获取url
"redirect_uri" => "https%3A%2F%2Fmusic.163.com%2Fback%2Fqq",//回调地址
"client_id" => "100495085",//网易云网站对应的ID
"cookielist" => array("MUSIC_U" => ""),//需要获取的Cookie值
"cookieignorelist" => array(),
"cookieTime" => 1267200,//24*60*60*15-8*60*60, Cookie有效时长,单位秒
"cookieEndTime" => 0 //cookie到期时间
),
通过Fiddler找到网易云音乐对应获取state的url:https://music.163.com/api/sns/authorize?snsType=5&clientType=web2&callbackType=Login&forcelogin=true
模拟请求该链接后通过正则匹配获取state的值。
2)授权第三方网页
通过POST请求https://graph.qq.com/oauth2.0/authorize传入最重要的client_id、redirect_uri、state、g_tk这4个参数,其中g_tk由p_skey计算得出。如果授权成功则会返回授权成功后的回调url。
3)获取回调网页Cookie
模拟请求上一步获取到的回调url即可获取到对应网站的cookie信息。
总结
通过以上流程即可全自动化的实现登录,并且可以在此基础上增加cookie到期时间判断,实现定时更新第三方网站的cookie。
以下为PHP版完整的第三方授权类,
本文仅供思路参考,严禁用于非法用途,转载请注明出处。