要使用qq互联授权登录,需要先去qq互联网站 https://connect.qq.com申请成为开发者,创建一个网站应用(注意网站的名称信息必须和备案信息一致,我之前几次就因为这个被拒绝了),同时需要填上网站的域名和回调地址
之后会得到一个APP ID
和APP Key
。
流程
qq登录和微信登录流程基本类似,都是以下步骤
- 跳转到授权页面,用户同意授权后获取code,code有效期10分钟
- 使用code获取access_token
- 使用access_token获取用户openid
- 使用access_token和openid获取用户信息
代码部分
封装成公共类如下,(使用方法)
<?php
/**
* qq授权登录获取用户信息
* @param $appid qq互联应用appid
* @param $appkey qq互联应用appkey
* @param $callback qq互联应用配置的回调地址
* @author codehui <admin@codehui.net> 2017-12-23
*/
class QqOauth {
const GET_AUTH_CODE_URL = "https://graph.qq.com/oauth2.0/authorize";
const GET_ACCESS_TOKEN_URL = "https://graph.qq.com/oauth2.0/token";
const GET_OPENID_URL = "https://graph.qq.com/oauth2.0/me";
const GET_USER_INFO_URL = "https://graph.qq.com/user/get_user_info";
private $appid;
private $appkey;
private $callback;
public function __construct() {
if (func_num_args() == 3) {
$this->appid = func_get_arg(0);
$this->appkey = func_get_arg(1);
$this->callback = func_get_arg(2);
} else {
$this->error('20001', '缺少必要参数');
}
}
/**
* qq授权页面
*/
public function qqLogin(){
// 生成唯一随机串防CSRF攻击
$state = md5(uniqid(rand(), TRUE));
session('state', $state);
$uri = $this->combineURL(self::GET_AUTH_CODE_URL, [
"response_type" => "code",
"client_id" => $this->appid,
"redirect_uri" => $this->callback,
"state" => $state,
"scope" => 'get_user_info' // 请求用户授权时向用户显示的可进行授权的列表
]);
header("Location:$uri");
}
/**
* 使用Authorization_Code获取Access_Token
* 如果用户成功登录并授权,则会跳转到指定的回调地址,并在redirect_uri地址后带上Authorization Code(10分钟有效期)和原始的state值
* @return array ['access_token'=>'授权令牌', 'expires_in'=>'有效期,单位秒','refresh_token'=>'续期令牌']
*/
public function getToken() {
// 验证state防止CSRF攻击
if (!$_REQUEST['state'] || $_REQUEST['state'] != session('state')){
$this->error = 20001;
}
$token_url = $this->combineURL(self::GET_ACCESS_TOKEN_URL, [
"grant_type" => "authorization_code",
"client_id" => $this->appid,
"redirect_uri" => urlencode($this->callback),
"client_secret" => $this->appkey,
"code" => $_REQUEST['code']
]);
$response = $this->httpsRequest($token_url);
if(strpos($response, "callback") !== false){
$lpos = strpos($response, "(");
$rpos = strrpos($response, ")");
$response = substr($response, $lpos + 1, $rpos - $lpos -1);
$msg = json_decode($response);
if(isset($msg->error)){
return $this->error($msg->error, $msg->error_description);
}
}
$params = [];
parse_str($response, $params);
return $params;
}
/**
* 获取openid
* @param $access_token 上一步获取到的access_token
* @return string
*/
public function getOpenid($access_token){
$graph_url = $this->combineURL(self::GET_OPENID_URL, [
'access_token' => $access_token
]);
$response = $this->httpsRequest($graph_url);
// 检测错误是否发生
if(strpos($response, "callback") !== false){
$lpos = strpos($response, "(");
$rpos = strrpos($response, ")");
$response = substr($response, $lpos + 1, $rpos - $lpos -1);
}
$user = json_decode($response);
if(isset($user->error)){
return $this->error($user->error, $user->error_description);
}
return $user->openid;
}
/**
* 获取用户信息
* @param $access_token 授权令牌
* @param $openid 用户openid
* @return array
*/
public function getUserinfo($access_token, $openid){
$get_userinfo_url = $this->combineURL(self::GET_USER_INFO_URL, [
'openid' => $openid,
'access_token' => $access_token,
'oauth_consumer_key' => $this->appid
]);
$response = $this->httpsRequest($get_userinfo_url);
$info = json_decode($response);
if(!isset($info->ret)) {
return $this->error('110500', '获取用户授权信息失败');
}
return $info;
}
/**
* 错误信息
* @param int $code 错误代码
* @param string $info 描述信息
* @return array
*/
public function error($code = 0, $msg = ''){
return [
'errcode' => $code,
'errmsg' => $msg
];
}
/**
* 拼接url
* @param string $baseURL 请求的url
* @param array $keysArr 参数列表数组
* @return string 返回拼接的url
*/
public function combineURL($baseURL, $keysArr) {
$combined = $baseURL . "?";
$valueArr = [];
foreach($keysArr as $key => $val){
$valueArr[] = "$key=$val";
}
$keyStr = implode("&", $valueArr);
$combined .= ($keyStr);
return $combined;
}
/**
* 获取服务器数据
* @param string $url 请求的url
* @return unknown 请求返回的内容
*/
public function httpsRequest($url) {
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
$output = curl_exec($curl);
curl_close($curl);
return $output;
}
}
使用方法
// 用户点击qq登录之后的操作,该方法会自动跳转到授权页面
function qq_login() {
$qq = new QqOauth('appid', 'appkey', 'http://***/oauth/qqCallBack');
$qq->qqLogin();
}
// 回调方法
public function qqCallBack(){
$qq = new QqOauth('appid', 'appkey', 'http://***/oauth/qqCallBack');
$token = $qq->getToken();
if(isset($token['errcode'])){
return json_encode($token);
}
$openid = $qq->getOpenid($token['access_token']);
if(isset($token['openid'])){
return json_encode($token);
}
$info = $qq->getUserinfo($token['access_token'], $openid);
return json_encode($info);
}
返回参数说明
参数说明 | 描述 |
---|---|
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 | 性别。 如果获取不到则默认返回”男” |