QQ互联的官网:https://connect.qq.com/
在创建应用的时候,基本信息要认真填,一般第二天就会审核好,我当时不知道怎么填,在这里浪费了好几天时间呢
此处网站名称必须得和备案的域名信息一致
网站回调地址很简单,备案过的域名就行了,但是注意回调域,要填该网站地址下的子级目录,回调域就是登录成功后的回调地址,当用户使用QQ登录成功时,会自动跳转到这个回调页面下,所以开发者可以在里面处理一些数据库或者是Session的操作
创建好了一个应用后,就可以获得APP ID和APP Key:
官方文档教程:https://wiki.connect.qq.com/准备工作_oauth2-0
我之所以在文章标题说我写的登录调用超简单,是因为我就只用到了三个文件(如下图所示),下载地址:https://download.csdn.net/download/jal517486222/11347475 , 文中也给出了这三个文件的完整代码。
如果去QQ互联网站上下载PHP版本demo的话,会发现demo中有好多个文件夹,文件夹里又有好几个文件,让人都看不下去。
如果你接入QQ只是为了获取用户的昵称、性别、头像这些不用授权的信息,那直接参考我的代码就行了,我的代码就是从官方demo的代码提炼出来的三个文件,绝对非常方便实用。
我是在登录页面和个人后台管理页面都放置了一个QQ登录的图片按钮,为的是让用户可以在个人后台点击按钮绑定QQ,绑定后以后在登录页面时就可以使用QQ登录了。这个QQ登录图片可以在QQ互联的网站上下载的。
这里代码比较简单,主要就是一个QQ登录的链接,所以这个代码我就没放到上面的QQ_Oauth文件夹里了。
关键代码如下:
<p>QQ绑定(开发中):
if(empty($openId)){//$openId是在数据库的users表中查看当前用户是不是已经绑定过了QQ
echo ' ';
}else{
echo $QQ_nick;
echo '解除绑定';
}
?>
</p>
<script>
function toLogin()
{
//以下为按钮点击事件的逻辑。注意这里要重新打开窗口
//否则后面跳转到QQ登录,授权页面时会直接缩小当前浏览器的窗口,而不是打开新窗口
var A=window.open("../QQ_Oauth/index.php","TencentLogin","width=700,height=500,menubar=0,scrollbars=1,resizable=1,status=1,titlebar=0,toolbar=0,location=1");
//定时判断是否新窗口已经被关闭了,如果被关闭了,则执行刷新页面的操作。
var loop = setInterval(function() {
if(A.closed) {
clearInterval(loop);
//alert('closed');
parent.location.reload();
}
}, 1);
}
function unbind(){
//解除QQ绑定
if(confirm("你确定要解除此账号当前绑定的QQ吗?")){
//我在这里的解绑操作就是清空了当前用户的users表中的openID字段就行了。
$.post("doPost.php?action=unbindQQ", {}, function(r){
if($.trim(r)==='success'){
alert("解除绑定成功!");
window.location.reload();
}else{
alert("解除绑定失败!");
}
});
}
}
</script>
其中doPost.php中只做了清空users表QQOpenId、QQ_nick字段的操作
if (isset($_GET['action']) && $_GET['action'] == 'unbindQQ') {
if (!(isset($_SESSION[$OJ_NAME . '_' . 'user_id']))) {//未登录
echo "error";
}else{
pdo_query("update users set QQOpenId = '', QQ_nick = '' where user_id = ?", $_SESSION[$OJ_NAME . '_' . 'user_id']);
echo "success";
}
}
<a href="#" onclick='toLogin()'><img src="image/QQ_login.png"></a>
<script>
function toLogin()
{
//以下为按钮点击事件的逻辑。注意这里要重新打开窗口
//否则后面跳转到QQ登录,授权页面时会直接缩小当前浏览器的窗口,而不是打开新窗口
var A=window.open("QQ_Oauth/index.php","TencentLogin", "width=700,height=500,menubar=0,scrollbars=1,resizable=1,status=1,titlebar=0,toolbar=0,location=1");
var loop = setInterval(function() {
if(A.closed) {
clearInterval(loop);
//alert('closed');
// parent.location.reload();
if($OJ_NEED_LOGIN)
echo "window.location.href='index.php';\n";
else
echo "history.go(-2);\n";
?>
}
}, 1);
}
</script>
@session_start();
//申请到的appid
$_SESSION["appid"] = 1*******4;
//申请到的appkey
$_SESSION["appkey"] = "7**********************3";
//QQ登录成功后跳转的地址,请确保地址真实可用,否则会导致登录失败。
//这个地址务必要细心填写,得和QQ互联网站上创建的应用的网站回调域一致,我当时就是因为这个没填好耽误了时间。
$_SESSION["callback"] = "http://acm.webturing.com/JudgeOnline/QQ_Oauth/callback.php";
//QQ授权api接口.按需调用
$_SESSION["scope"] = "get_user_info";//get_user_info是不需要用户授权的,可以直接使用
?>
我们可以看到上面的登录按钮跳转的是新窗口打开QQ_Oauth/index.php页面,
当点击上面的QQ登录按钮的时候,将会打开登录页面
require_once("config.php");
@session_start();
function qq_login($appid, $scope, $callback)
{
$_SESSION['state'] = md5(uniqid(rand(), TRUE)); //CSRF protection
$login_url = "https://graph.qq.com/oauth2.0/authorize?response_type=code&client_id="
. $appid . "&redirect_uri=" . urlencode($callback)
. "&state=" . $_SESSION['state']
. "&scope=".$scope;
header("Location:$login_url");
}
//用户点击qq登录按钮调用此函数
qq_login($_SESSION["appid"], $_SESSION["scope"], $_SESSION["callback"]);
?>
可以从上面代码中看到我们是请求了https://graph.qq.com/oauth2.0/authorize
去生成QQ登录页面的,并且我们要带上正确的$_SESSION["appid"]
, $_SESSION["scope"]
, $_SESSION["callback"]
这三个参数,这三个参数都是在config.php中配的。
用户登录成功后,则会自动进入QQ_Oauth/callback.php这个回调页面
require_once("config.php");
include_once "../include/db_info.inc.php";
function qq_callback()
{
//debug
// print_r($_REQUEST);
// print_r($_SESSION);
if($_REQUEST['state'] == $_SESSION['state']) //csrf
{
$token_url = "https://graph.qq.com/oauth2.0/token?grant_type=authorization_code&"
. "client_id=" . $_SESSION["appid"]. "&redirect_uri=" . urlencode($_SESSION["callback"])
. "&client_secret=" . $_SESSION["appkey"]. "&code=" . $_REQUEST["code"];
$response = file_get_contents($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))
{
echo "error:
" . $msg->error;
echo "msg :
" . $msg->error_description;
exit;
}
}
$params = array();
parse_str($response, $params);
//debug
//print_r($params);
//set access token to session
$_SESSION["access_token"] = $params["access_token"];
}
else
{
echo("The state does not match. You may be a victim of CSRF.");
}
}
function get_openid()
{
$graph_url = "https://graph.qq.com/oauth2.0/me?access_token="
. $_SESSION['access_token'];
$str = file_get_contents($graph_url);
if (strpos($str, "callback") !== false)
{
$lpos = strpos($str, "(");
$rpos = strrpos($str, ")");
$str = substr($str, $lpos + 1, $rpos - $lpos -1);
}
$user = json_decode($str);
if (isset($user->error))
{
echo "error:
" . $user->error;
echo "msg :
" . $user->error_description;
exit;
}
//debug
//echo("Hello " . $user->openid);
//set openid to session
$_SESSION["openid"] = $user->openid;
}
function get_user_info()
{
$get_user_info = "https://graph.qq.com/user/get_user_info?"
. "access_token=" . $_SESSION['access_token']
. "&oauth_consumer_key=" . $_SESSION["appid"]
. "&openid=" . $_SESSION["openid"]
. "&format=json";
$info = file_get_contents($get_user_info);
$arr = json_decode($info, true);
return $arr;
}
//QQ登录成功后的回调地址,主要保存access token
qq_callback();
//获取用户标示id
get_openid();
//获取用户基本资料
$arr = get_user_info();
if (!(isset($_SESSION[$OJ_NAME . '_' . 'user_id']))) {//未登录,说明在进行登录操作,通过OpenID查得User_id进行登录,更新这个用户的session
$openid_users = pdo_query("select user_id from users where QQOpenId = ?", $_SESSION["openid"]);
if(count($openid_users) > 0){
$user_id = $openid_users[0]['user_id'];
$_SESSION[$OJ_NAME.'_'.'user_id']=$user_id;
$result=pdo_query("SELECT `rightstr` FROM `privilege` WHERE `user_id`=?",$user_id);
foreach ($result as $row)
$_SESSION[$OJ_NAME.'_'.$row['rightstr']]=true;
$sql = "update users set accesstime = NOW() where user_id=?";
$result = pdo_query($sql, $user_id);
if ($OJ_SIMPLE_LOGIN){
$session_id = session_id();
pdo_query("update users set session_id = ? where user_id = ?", $session_id, $user_id);
}
}else{
echo "";
}
}else{//已登录,说明在进行绑定操作,更新OpenID和QQNick
$openid_users = pdo_query("select user_id from users where QQOpenId = ?", $_SESSION["openid"]);
if(count($openid_users) > 0){
echo "";
}else{
$user_id = $_SESSION[$OJ_NAME . '_' . 'user_id'];
pdo_query("update users set QQOpenId = ?, QQ_nick = ? where user_id = ?", $_SESSION["openid"], $arr['nickname'], $user_id);
}
}
//print_r($_SESSION);
echo "";
?>
我在判断
$_REQUEST['state'] == $_SESSION['state']
还遇到了一个小坑,记录在此了:域名不一致导致CSRF验证失败
当callback.php执行结束后,这个窗口就会关闭,如果之前用户在进行登录操作,则是完成了登录,页面将会发生跳转,如果用户之前是在进行绑定操作,则是自动刷新页面:
这就表示已经绑定成功了,下次就可以用QQ登录了。
- 我在代码中用到的
pdo_query()
函数是我自己封装的数据库执行代码,并不是PHP内置的函数,如果有网友参考我的代码,请自己手动修改对应的数据库操作部分代码。
- 我们无法直接获取用户的QQ号,但可以获取用户的
OpenID
,OpenID可以作为用户的QQ号的唯一标识,与用户QQ号一一对应。
如果指正或咨询之处,可以直接在评论留言,我一般都是秒回的,也可以QQ我哟:517486222