要使用qq互联授权登录,需要先去qq互联网站 https://connect.qq.com申请成为开发者,创建一个网站应用(注意网站的名称信息必须和备案信息一致,我之前几次就因为这个被拒绝了),同时需要填上网站的域名和回调地址

之后会得到一个APP IDAPP Key

流程

qq登录和微信登录流程基本类似,都是以下步骤:joy:

  • 跳转到授权页面,用户同意授权后获取code,code有效期10分钟
  • 使用code获取access_token
  • 使用access_token获取用户openid
  • 使用access_token和openid获取用户信息

代码部分

封装成公共类如下,(使用方法)

  1. <?php
  2. /**
  3. * qq授权登录获取用户信息
  4. * @param $appid qq互联应用appid
  5. * @param $appkey qq互联应用appkey
  6. * @param $callback qq互联应用配置的回调地址
  7. * @author codehui <admin@codehui.net> 2017-12-23
  8. */
  9. class QqOauth {
  10. const GET_AUTH_CODE_URL = "https://graph.qq.com/oauth2.0/authorize";
  11. const GET_ACCESS_TOKEN_URL = "https://graph.qq.com/oauth2.0/token";
  12. const GET_OPENID_URL = "https://graph.qq.com/oauth2.0/me";
  13. const GET_USER_INFO_URL = "https://graph.qq.com/user/get_user_info";
  14. private $appid;
  15. private $appkey;
  16. private $callback;
  17. public function __construct() {
  18. if (func_num_args() == 3) {
  19. $this->appid = func_get_arg(0);
  20. $this->appkey = func_get_arg(1);
  21. $this->callback = func_get_arg(2);
  22. } else {
  23. $this->error('20001', '缺少必要参数');
  24. }
  25. }
  26. /**
  27. * qq授权页面
  28. */
  29. public function qqLogin(){
  30. // 生成唯一随机串防CSRF攻击
  31. $state = md5(uniqid(rand(), TRUE));
  32. session('state', $state);
  33. $uri = $this->combineURL(self::GET_AUTH_CODE_URL, [
  34. "response_type" => "code",
  35. "client_id" => $this->appid,
  36. "redirect_uri" => $this->callback,
  37. "state" => $state,
  38. "scope" => 'get_user_info' // 请求用户授权时向用户显示的可进行授权的列表
  39. ]);
  40. header("Location:$uri");
  41. }
  42. /**
  43. * 使用Authorization_Code获取Access_Token
  44. * 如果用户成功登录并授权,则会跳转到指定的回调地址,并在redirect_uri地址后带上Authorization Code(10分钟有效期)和原始的state值
  45. * @return array ['access_token'=>'授权令牌', 'expires_in'=>'有效期,单位秒','refresh_token'=>'续期令牌']
  46. */
  47. public function getToken() {
  48. // 验证state防止CSRF攻击
  49. if (!$_REQUEST['state'] || $_REQUEST['state'] != session('state')){
  50. $this->error = 20001;
  51. }
  52. $token_url = $this->combineURL(self::GET_ACCESS_TOKEN_URL, [
  53. "grant_type" => "authorization_code",
  54. "client_id" => $this->appid,
  55. "redirect_uri" => urlencode($this->callback),
  56. "client_secret" => $this->appkey,
  57. "code" => $_REQUEST['code']
  58. ]);
  59. $response = $this->httpsRequest($token_url);
  60. if(strpos($response, "callback") !== false){
  61. $lpos = strpos($response, "(");
  62. $rpos = strrpos($response, ")");
  63. $response = substr($response, $lpos + 1, $rpos - $lpos -1);
  64. $msg = json_decode($response);
  65. if(isset($msg->error)){
  66. return $this->error($msg->error, $msg->error_description);
  67. }
  68. }
  69. $params = [];
  70. parse_str($response, $params);
  71. return $params;
  72. }
  73. /**
  74. * 获取openid
  75. * @param $access_token 上一步获取到的access_token
  76. * @return string
  77. */
  78. public function getOpenid($access_token){
  79. $graph_url = $this->combineURL(self::GET_OPENID_URL, [
  80. 'access_token' => $access_token
  81. ]);
  82. $response = $this->httpsRequest($graph_url);
  83. // 检测错误是否发生
  84. if(strpos($response, "callback") !== false){
  85. $lpos = strpos($response, "(");
  86. $rpos = strrpos($response, ")");
  87. $response = substr($response, $lpos + 1, $rpos - $lpos -1);
  88. }
  89. $user = json_decode($response);
  90. if(isset($user->error)){
  91. return $this->error($user->error, $user->error_description);
  92. }
  93. return $user->openid;
  94. }
  95. /**
  96. * 获取用户信息
  97. * @param $access_token 授权令牌
  98. * @param $openid 用户openid
  99. * @return array
  100. */
  101. public function getUserinfo($access_token, $openid){
  102. $get_userinfo_url = $this->combineURL(self::GET_USER_INFO_URL, [
  103. 'openid' => $openid,
  104. 'access_token' => $access_token,
  105. 'oauth_consumer_key' => $this->appid
  106. ]);
  107. $response = $this->httpsRequest($get_userinfo_url);
  108. $info = json_decode($response);
  109. if(!isset($info->ret)) {
  110. return $this->error('110500', '获取用户授权信息失败');
  111. }
  112. return $info;
  113. }
  114. /**
  115. * 错误信息
  116. * @param int $code 错误代码
  117. * @param string $info 描述信息
  118. * @return array
  119. */
  120. public function error($code = 0, $msg = ''){
  121. return [
  122. 'errcode' => $code,
  123. 'errmsg' => $msg
  124. ];
  125. }
  126. /**
  127. * 拼接url
  128. * @param string $baseURL 请求的url
  129. * @param array $keysArr 参数列表数组
  130. * @return string 返回拼接的url
  131. */
  132. public function combineURL($baseURL, $keysArr) {
  133. $combined = $baseURL . "?";
  134. $valueArr = [];
  135. foreach($keysArr as $key => $val){
  136. $valueArr[] = "$key=$val";
  137. }
  138. $keyStr = implode("&", $valueArr);
  139. $combined .= ($keyStr);
  140. return $combined;
  141. }
  142. /**
  143. * 获取服务器数据
  144. * @param string $url 请求的url
  145. * @return unknown 请求返回的内容
  146. */
  147. public function httpsRequest($url) {
  148. $curl = curl_init();
  149. curl_setopt($curl, CURLOPT_URL, $url);
  150. curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
  151. curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE);
  152. curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
  153. $output = curl_exec($curl);
  154. curl_close($curl);
  155. return $output;
  156. }
  157. }
使用方法
  1. // 用户点击qq登录之后的操作,该方法会自动跳转到授权页面
  2. function qq_login() {
  3. $qq = new QqOauth('appid', 'appkey', 'http://***/oauth/qqCallBack');
  4. $qq->qqLogin();
  5. }
  6. // 回调方法
  7. public function qqCallBack(){
  8. $qq = new QqOauth('appid', 'appkey', 'http://***/oauth/qqCallBack');
  9. $token = $qq->getToken();
  10. if(isset($token['errcode'])){
  11. return json_encode($token);
  12. }
  13. $openid = $qq->getOpenid($token['access_token']);
  14. if(isset($token['openid'])){
  15. return json_encode($token);
  16. }
  17. $info = $qq->getUserinfo($token['access_token'], $openid);
  18. return json_encode($info);
  19. }
返回参数说明
参数说明 描述
ret 返回码
msg 如果ret<0,会有相应的错误信息提示,返回数据全部用UTF-8编码。
nickname 用户在QQ空间的昵称。
figureurl 大小为30×30像素的QQ空间头像URL。
figureurl_1 大小为50×50像素的QQ空间头像URL。
figureurl_2 大小为100×100像素的QQ空间头像URL。
figureurl_qq_1 大小为40×40像素的QQ头像URL。
figureurl_qq_2 大小为100×100像素的QQ头像URL。需要注意,不是所有的用户都拥有QQ的100x100的头像,但40x40像素则是一定会有。
gender 性别。 如果获取不到则默认返回”男”