wechatauth.class.php 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  1. <?php
  2. /**
  3. * 微信公众平台PHP-SDK
  4. * Wechatauth为非官方微信登陆API
  5. * 用户通过扫描网页提供的二维码实现登陆信息获取
  6. * 主要实现如下功能:
  7. * get_login_code() 获取登陆授权码, 通过授权码才能获取二维码
  8. * get_code_image($code='') 将上面获取的授权码转换为图片二维码
  9. * verify_code() 鉴定是否登陆成功,返回200为最终授权成功.
  10. * get_login_info() 鉴定成功后调用此方法即可获取用户基本信息
  11. * get_avatar($url) 获取用户头像图片数据
  12. * @author dodge <dodgepudding@gmail.com>
  13. * @link https://github.com/dodgepudding/wechat-php-sdk
  14. * @version 1.1
  15. *
  16. */
  17. include "snoopy.class.php";
  18. class Wechatauth
  19. {
  20. private $cookie;
  21. private $skey;
  22. private $_cookiename;
  23. private $_cookieexpired = 3600;
  24. private $_account = 'test';
  25. private $_datapath = './data/cookie_';
  26. private $debug;
  27. private $_logcallback;
  28. public $login_user; //当前登陆用户, 调用get_login_info后获取
  29. public function __construct($options)
  30. {
  31. $this->_account = isset($options['account'])?$options['account']:'';
  32. $this->_datapath = isset($options['datapath'])?$options['datapath']:$this->_datapath;
  33. $this->debug = isset($options['debug'])?$options['debug']:false;
  34. $this->_logcallback = isset($options['logcallback'])?$options['logcallback']:false;
  35. $this->_cookiename = $this->_datapath.$this->_account;
  36. $this->getCookie($this->_cookiename);
  37. }
  38. /**
  39. * 把cookie写入缓存
  40. * @param string $filename 缓存文件名
  41. * @param string $content 文件内容
  42. * @return bool
  43. */
  44. public function saveCookie($filename,$content){
  45. return file_put_contents($filename,$content);
  46. }
  47. /**
  48. * 读取cookie缓存内容
  49. * @param string $filename 缓存文件名
  50. * @return string cookie
  51. */
  52. public function getCookie($filename){
  53. if (file_exists($filename)) {
  54. $mtime = filemtime($filename);
  55. if ($mtime<time()-$this->_cookieexpired) return false;
  56. $data = file_get_contents($filename);
  57. if ($data) $this->cookie = $data;
  58. }
  59. return $this->cookie;
  60. }
  61. /*
  62. * 删除cookie
  63. */
  64. public function deleteCookie($filename) {
  65. $this->cookie = '';
  66. @unlink($filename);
  67. return true;
  68. }
  69. private function log($log){
  70. if ($this->debug && function_exists($this->_logcallback)) {
  71. if (is_array($log)) $log = print_r($log,true);
  72. return call_user_func($this->_logcallback,$log);
  73. }
  74. }
  75. /**
  76. * 获取登陆二维码对应的授权码
  77. */
  78. public function get_login_code(){
  79. if ($this->_logincode) return $this->_logincode;
  80. $t = time().strval(mt_rand(100,999));
  81. $codeurl = 'https://login.weixin.qq.com/jslogin?appid=wx782c26e4c19acffb&redirect_uri=https%3A%2F%2Fwx.qq.com%2Fcgi-bin%2Fmmwebwx-bin%2Fwebwxnewloginpage&fun=new&lang=zh_CN&_='.$t;
  82. $send_snoopy = new Snoopy;
  83. $send_snoopy->fetch($codeurl);
  84. $result = $send_snoopy->results;
  85. if ($result) {
  86. preg_match("/window.QRLogin.uuid\s+=\s+\"([^\"]+)\"/",$result,$matches);
  87. if(count($matches)>1) {
  88. $this->_logincode = $matches[1];
  89. $_SESSION['login_step'] = 0;
  90. return $this->_logincode;
  91. }
  92. }
  93. return $result;
  94. }
  95. /**
  96. * 通过授权码获取对应的二维码图片地址
  97. * @param string $code
  98. * @return string image url
  99. */
  100. public function get_code_image($code=''){
  101. if ($code=='') $code = $this->_logincode;
  102. if (!$code) return false;
  103. return 'http://login.weixin.qq.com/qrcode/'.$this->_logincode.'?t=webwx';
  104. }
  105. /**
  106. * 设置二维码对应的授权码
  107. * @param string $code
  108. * @return class $this
  109. */
  110. public function set_login_code($code) {
  111. $this->_logincode = $code;
  112. return $this;
  113. }
  114. /**
  115. * 二维码登陆验证
  116. *
  117. * @return status:
  118. * >=400: invaild code; 408: not auth and wait, 400,401: not valid or expired
  119. * 201: just scaned but not confirm
  120. * 200: confirm then you can get user info
  121. */
  122. public function verify_code() {
  123. if (!$this->_logincode) return false;
  124. $t = time().strval(mt_rand(100,999));
  125. $url = 'https://login.weixin.qq.com/cgi-bin/mmwebwx-bin/login?uuid='.$this->_logincode.'&tip=0&_='.$t;
  126. $send_snoopy = new Snoopy;
  127. $send_snoopy->referer = "https://wx.qq.com/";
  128. $send_snoopy->fetch($url);
  129. $result = $send_snoopy->results;
  130. $this->log('step1:'.$result);
  131. if ($result) {
  132. preg_match("/window\.code=(\d+)/",$result,$matches);
  133. if(count($matches)>1) {
  134. $status = intval($matches[1]);
  135. if ($status==201) $_SESSION['login_step'] = 1;
  136. if ($status==200) {
  137. preg_match("/ticket=([0-9a-z-_]+)&lang=zh_CN&scan=(\d+)/",$result,$matches);
  138. preg_match("/window.redirect_uri=\"([^\"]+)\"/",$result,$matcheurl);
  139. $this->log('step2:'.print_r($matches,true));
  140. if (count($matcheurl)>1) {
  141. $ticket = $matches[1];
  142. $scan = $matches[2];
  143. //$loginurl = 'https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxnewloginpage?ticket='.$ticket.'&lang=zh_CN&scan='.$scan.'&fun=new';
  144. $loginurl = str_replace("wx.qq.com", "wx2.qq.com", $matcheurl[1]).'&fun=old';
  145. $urlpart = parse_url($loginurl);
  146. $send_snoopy = new Snoopy;
  147. $send_snoopy->referer = "https://{$urlpart['host']}/cgi-bin/mmwebwx-bin/webwxindex?t=chat";
  148. $send_snoopy->fetch($loginurl);
  149. $result = $send_snoopy->results;
  150. $xml = simplexml_load_string($result);
  151. if ($xml->ret=="0") $this->skey = $xml->skey;
  152. foreach ($send_snoopy->headers as $key => $value) {
  153. $value = trim($value);
  154. if(strpos($value,'Set-Cookie: ') !== false){
  155. $tmp = str_replace("Set-Cookie: ","",$value);
  156. $tmparray = explode(';', $tmp);
  157. $item = trim($tmparray[0]);
  158. $cookie.=$item.';';
  159. }
  160. }
  161. $cookie .="Domain=.qq.com;";
  162. $this->cookie = $cookie;
  163. $this->log('step3:'.$loginurl.';cookie:'.$cookie.';respond:'.$result);
  164. $this->saveCookie($this->_cookiename,$this->cookie);
  165. }
  166. }
  167. return $status;
  168. }
  169. }
  170. return false;
  171. }
  172. /**
  173. * 获取登陆的cookie
  174. *
  175. * @param bool $is_array 是否以数值方式返回,默认否,返回字符串
  176. * @return string|array
  177. */
  178. public function get_login_cookie($is_array = false){
  179. if (!$is_array) return $this->cookie;
  180. $c_arr = explode(';',$this->cookie);
  181. $cookie = array();
  182. foreach($c_arr as $item) {
  183. $kitem = explode('=',trim($item));
  184. if (count($kitem)>1) {
  185. $key = trim($kitem[0]);
  186. $val = trim($kitem[1]);
  187. if (!empty($val)) $cookie[$key] = $val;
  188. }
  189. }
  190. return $cookie;
  191. }
  192. /**
  193. * 授权登陆后获取用户登陆信息
  194. */
  195. public function get_login_info(){
  196. if (!$this->cookie) return false;
  197. $t = time().strval(mt_rand(100,999));
  198. $send_snoopy = new Snoopy;
  199. $submit = 'https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxinit?r='.$t.'&skey='.urlencode($this->skey);
  200. $send_snoopy->rawheaders['Cookie']= $this->cookie;
  201. $send_snoopy->referer = "https://wx2.qq.com/";
  202. $citems = $this->get_login_cookie(true);
  203. $post = array(
  204. "BaseRequest"=>array(
  205. array(
  206. "Uin"=>$citems['wxuin'],
  207. "Sid"=>$citems['wxsid'],
  208. "Skey"=>$this->skey,
  209. "DeviceID"=>''
  210. )
  211. )
  212. );
  213. $send_snoopy->submit($submit,json_encode($post));
  214. $this->log('login_info:'.$send_snoopy->results);
  215. $result = json_decode($send_snoopy->results,true);
  216. if ($result['BaseResponse']['Ret']<0) return false;
  217. $this->_login_user = $result['User'];
  218. return $result;
  219. }
  220. /**
  221. * 获取头像
  222. * @param string $url 传入从用户信息接口获取到的头像地址
  223. */
  224. public function get_avatar($url) {
  225. if (!$this->cookie) return false;
  226. if (strpos($url, 'http')===false) {
  227. $url = 'http://wx2.qq.com'.$url;
  228. }
  229. $send_snoopy = new Snoopy;
  230. $send_snoopy->rawheaders['Cookie']= $this->cookie;
  231. $send_snoopy->referer = "https://wx2.qq.com/";
  232. $send_snoopy->fetch($url);
  233. $result = $send_snoopy->results;
  234. if ($result)
  235. return $result;
  236. else
  237. return false;
  238. }
  239. /**
  240. * 登出当前登陆用户
  241. */
  242. public function logout(){
  243. if (!$this->cookie) return false;
  244. preg_match("/wxuin=(\w+);/",$this->cookie,$matches);
  245. if (count($matches)>1) $uid = $matches[1];
  246. preg_match("/wxsid=(\w+);/",$this->cookie,$matches);
  247. if (count($matches)>1) $sid = $matches[1];
  248. $this->log('logout: uid='.$uid.';sid='.$sid);
  249. $send_snoopy = new Snoopy;
  250. $submit = 'https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxlogout?redirect=1&type=1';
  251. $send_snoopy->rawheaders['Cookie']= $this->cookie;
  252. $send_snoopy->referer = "https://wx2.qq.com/";
  253. $send_snoopy->submit($submit,array('uin'=>$uid,'sid'=>$sid));
  254. $this->deleteCookie($this->_cookiename);
  255. return true;
  256. }
  257. }