今天是我在第一个公司实习的倒计时2天了,已经快3个月了,由于时间关系,月底就辞职准备做毕业设计了。最近一个任务是给网站加 qq 登录和 微博 登录 的功能,前2天一直没搞定,昨晚有了点思绪,到今天中午就把 qq 登录的调通了,完成了一个后另一个就简单多了,下午就把微博登录的也完成了。主要是 qq 登录碰到的问题比较多,也可以说是没对没做过的东西莫名有种恐惧吧,百度了好多,也下了 qq 互联官网(点击打开链接下载sdk),我使用的是 php ,所以下了 PHP SDK v2.1 的版本。
首先登录 qq 互联官网(点击打开链接)之后点击 应用管理 进行公司接入或个人接入的信息填写,填写之后提交审核,可以获取AppId、AppKey,自己填写回调地址(登录qq成功后的跳转地址),未通过审核的话只能由此申请的 qq 账号进行测试登录。公司的话最好采用公用的一个 qq 账号而不是某个员工的账号,以免员工离职造成不必要的麻烦。
接下来我一开始是直接使用下载下来的封装好的 php_sdk,因为觉得很方便,不需了解其运行流程,只需简单调用几个函数即可。流程如下(可以看 doc/QC SDK for php 2.0文档.doc):
1、先将 sdk 文件夹上传至服务器,再执行 install/文件夹下的 index.php 设置配置项(appId、appKey、callback),执行成功之后检查跟 install 同级目录下 的API/comm/下是否存在 inc.php 就是刚才执行成功生成的配置文件。没有的话检查文件的权限。
2、上述配置文件生成后就可以将 install 文件夹删除,只需要 API 文件夹即可。
3、调用登录接口
<?php
require_once("你的路径/API/qqConnectAPI.php");
$qc = new QC();
$qc->qq_login();//转到 qq 登录页面,登录后直接跳到之前在qq互联官网上填写的回调地址,逻辑代码需要在回调地址的函数内进行处理。
?>
4、获得access_token,在callback页面中使用 $qc->qq_callback() 返回access_token, $qc->get_openid() 返回 openid
5、获取了 openid 之后,因为这个值是每个 qq 账号唯一的标识,因此你可以将它与网站的登录记录表中的一个记录进行关联(比如会员id),再处理登录的判断。
以下是我遇到的问题:
我设置完配置文件参数之后,登录 qq 账号后跳转报错码为 100010,百度之后发现是回调地址设置必须是具体到页面,不能只是域名,尝试将回调地址改成具体 域名/user.php 后就不报错了。本以为可以了,结果接下来报错什么 state 不匹配,code 参数丢失,真的是一头雾水,反正错误是跟你说了,但我还是一脸懵逼。浪费了好多时间后终于想到去看看 sdk 文件的代码实现,到具体代码进行调试,果然发现登录完 qq 后跳转到回调地址,获取到了 code ,但是代码使用 $_REQUEST['code'] 来获取参数 code,当然 $_GET['code']也可以,但是我一打印出来查看发现都是空,结果程序当然进行不下去了 。再仔细一看发现我登录之后跳转到的地址不是官方显示的这样:
官方这样阐述 => 例如回调地址是:www.xx.com/my.php,则会跳转到:
http://www.xx.com/my.php?code=520DD95263C1CFEA0870FBB66E******
我的居然是这样:http://x.xx.xxx.co/user.php?action=login&_referer=http%3A%2F%2Fm.xx.xxx.co%2Fuser.php%3Fcode%3D25F422E08D0695721E1E0103984E943F%26state%3Dc5423cb755920a25487474997c80fbb2
而且结果是 后半部分参数是urlencode 的地址参数,还多了http头_referer 的 部分,所以它后面还带了一个 (%3F)“?” 符号,导致code参数获取不到,于是我就只能先将地址中的参数($_SERVER['QUERY_STRING'])进行urldecode再将问号(?)转为(&),最后利用 parse_str($res,$res); 将字符串转换成数组 ,再取 $res['code'] 和 $res['state'] 就可以了。具体代码见下面。
上面这样处理总觉得怪怪的,上面 qq互联上设置的回调地址是 http://x.xx.xx.co/user.php,然后我代码也是这样设置的回调函数。
但是我这边网站代码控制动作跳转是由参数 action=xxx 决定的,于是我想让他登录后还是跳转到一开始执行登录代码的函数来处理逻辑,现在没加指定 action 就会默认跳到其他处理函数的内部,导致我一开始使用qq登录的处理函数不是回调的函数地址,所以导致逻辑代码分离了,感觉这样不好。(听起来比较绕,哎--)<<==>>现在我一开始执行qq登录的地址是 http://x.xx.xx.co/user.php?action=loginByQQ ,然后执行了登录后一跳转就到原先设置的回调地址去处理接下来的逻辑代码了,由于我上面的回调地址( http://x.xx.xx.co/user.php)没加 action 所以会跳到其他函数进行处理。于是想在qq互联官网上的回调地址上加上 ?action=loginByQQ ,结果发现不行,后来只能尝试在我代码这里改回调地址了,因为明确要求代码中的回调地址必须跟qq互联上设置的回调地址一致,但是我唯有尝试了。结果是出乎我意料的,居然可以了,更关键 的是解决了上面返回 code 时地址栏携带 _referer 头的 bug,真是神奇!这时地址就是回调地址带上参数了,直接可以使用 $_REQUEST['code'] 或 $_GET['code'] 获取get参数了,所以代码就能成功运行了。就这样我解决了 QQ 登录,其实发现没啥东西,就是使用了 sdk 反而让我忽略了一些错误,最后我直接在官网地址(官方示例代码)拷下来修改了。不用他的 sdk 了。
虽然不太能解释其中的原因,但是经过测试发现,只要在代码这边的回调地址后面加上 ?action=xxx 之后,qq 登录成功的跳转地址就不会包含 _referer 的内容了,xxx 内容不固定的,相当于处理函数的名字。(我这边是根据 action 来跳转到指定方法名函数进行处理的!)正常的跳转地址变成:http://x.xx.xxx.co/user.php?action=xxx&code=DCC5A2C605CFB***188ED730C27E5612&state=ea4df408f89ec888***6d936ab17cce6 。
还有就是未审核通过的话只能使用申请的账号登录测试。
具体我的代码如下:
qqConfig.php:
<?php
header('Content-Type: text/html; charset=UTF-8');
/*
* 从connect.qq.com申请获取AppId,AppKey并填写AppId,AppKey,callBack
* 再配置以下三个参数即可
*/
//应用的APPID
define('APPID',"100****55");
//应用的APPKEY
define('APPKEY',"02a639719bda841a7******7da02fdd8");
//成功授权后的回调地址
define('CALLBACKURL',"http://m.dev.booen.co/user.php?action=loginByQQ");
qqConnectAPI.php:
<?php
session_start();
include_once( 'qqConfig.php' );
//Step1:获取Authorization Code 当你使用 $_REQUEST['code']获取不到值时可以试试去掉下面的注释进行处理。
/*以下处理是当地址栏出现_referer时才需要做的处理 (当在qq互联那边设置的回调地址(不含?param=xxx)然后这边的回调地址也一致时,经过测试登录Qq后跳转的地址会有_referer头,所以参数不好直接使用$_REQUEST取了,需要做以下处理;后来在这边将回调地址手动加上?param=xxx之后虽然与qq互联那边设置的回调地址不一致但是可以运行也刚好解决了登录后跳转带_referer头部的问题。)
$queryStr = urldecode($_SERVER['QUERY_STRING']);
$res = str_replace('?','&',$queryStr);
parse_str($res,$res);//将字符串转换成数组
$code = $res['code'];
$state = $res['state'];
*/
$code = $_REQUEST['code'];
$state = $_REQUEST['state'];
if(empty($code))
{
//state参数用于防止CSRF攻击,成功授权后回调时会原样带回
$_SESSION['state'] = md5(uniqid(rand(), TRUE));
//拼接URL
$dialog_url = "https://graph.qq.com/oauth2.0/authorize?response_type=code&client_id="
. APPID . "&redirect_uri=" . urlencode(CALLBACKURL) . "&state="
. $_SESSION['state'];
header("Location:$dialog_url");
}
//Step2:通过Authorization Code获取Access Token
if($state == $_SESSION['state'])
{
//拼接URL
$token_url = "https://graph.qq.com/oauth2.0/token?grant_type=authorization_code&"
. "client_id=" . APPID . "&redirect_uri=" . urlencode(CALLBACKURL)
. "&client_secret=" . APPKEY . "&code=" . $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 "<h3>error:</h3>" . $msg->error;
echo "<h3>msg :</h3>" . $msg->error_description;
exit;
}
}
//Step3:使用Access Token来获取用户的OpenID
$params = array();
parse_str($response, $params);
$graph_url = "https://graph.qq.com/oauth2.0/me?access_token=" . $params['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 "<h3>error:</h3>" . $user->error;
echo "<h3>msg :</h3>" . $user->error_description;
exit;
}
/* 获取Qq信息 */
$getUserQQInfo_url = "https://graph.qq.com/user/get_user_info?access_token=" . $params['access_token'] . "&oauth_consumer_key=" . APPID . "&openid=" . $user->openid ;
$userQQInfo = json_decode(file_get_contents($getUserQQInfo_url));
//var_dump($userQQInfo->nickname);
$info = array(
'nickname' => $userQQInfo->nickname,
'openId' => $user->openid
);
$inkey['nickname'] = $userQQInfo->nickname;
$inkey['openId'] = $user->openid;
}
else
{
echo("The state does not match. You may be a victim of CSRF.");
break;
}
现在终于大功告成了,主要分三步:先获取 code,再通过 code 获取 access_token,最后通过 access_token 获取 openid。最后就是你自己处理登录的存储数据了。
接下来就是新浪微博的登录了,同样需要登录 open.weibo.com 登录微博账号进行网站接入前的资料填写申请审核,先获取 appId、appSecret,填写回调地址。sdk 的下载地址:点击打开链接。根据版本自己选择。
这个还好,没那么多限制,可以直接在 回调地址 设置 http://x.xx.xxx.co/user.php?action=loginByWeibo (就是可以直接加 ? 带参数)。
然后我看了下下载的 php_sdk ,如下图,每个文件看一下,最后我选取了 callback.php 和 config.php 和 saetv2.ex.class.php 进行修改并使用。
这个的话代码里面的回调地址必须跟申请的网站应用那边设置的回调地址一致才行,不能多任何东西。
其他没啥大问题,直接贴代码了。
weiboConfig.php:
<?php
header('Content-Type: text/html; charset=UTF-8');
define( "WB_AKEY" , '3358***710' );
define( "WB_SKEY" , 'af8d801cecc*****c499878ea**ae***' );
define( "WB_CALLBACK_URL" , 'http://x.xx.xxx.co/user.php?action=loginByWeibo' );
callback.php:
<?php
session_start();
include_once( 'weiboConfig.php' );
include_once( 'saetv2.ex.class.php' );
$o = new SaeTOAuthV2( WB_AKEY , WB_SKEY );
if (!isset($_REQUEST['code'])) {//为空则跳转到新浪微博登录页面
$getCode_url = $o->getAuthorizeURL( WB_CALLBACK_URL );
header("Location:$getCode_url");
}else{
$keys = array();
$keys['code'] = $_REQUEST['code'];
$keys['redirect_uri'] = WB_CALLBACK_URL;
try {
$token = $o->getAccessToken( 'code', $keys ) ;//获取access_token
} catch (OAuthException $e) {
}
}
if ($token) {
$_SESSION['token'] = $token;
$inkey['accessToken'] = $token['access_token'];
/*
$c = new SaeTClientV2( WB_AKEY , WB_SKEY , $_SESSION['token']['access_token'] );
$ms = $c->home_timeline(); // done
$uid_get = $c->get_uid();
$uid = $uid_get['uid'];
$user_message = $c->show_user_by_id( $uid);//根据ID获取用户等基本信息
var_dump($user_message);
*/
} else {
//break;
}
?>
saetv2.ex.class.php:
代码没改动就不贴代码了。。
好了!以上就是所有内容了,写了好久终于把这篇博文写完了!!感觉对新的东西刚接触时是有点恐惧,我在做的过程一直错,百度好久都收效甚微,几次想放弃,后来还是坚持了!希望以后能继续坚持!直到问题解决!(好了看-柒个我-去了,拜。)