这篇文章主要讲在使用PHP对接天猫精灵AliGennie平台时,Oauth对接出现如下图所出现的问题的解决方式:
因为本人也是刚接触这方面的内容在对接时也是参考某篇文章一步步做下来的,但是在跟着一步步做下来以后,发现在进行Oauth2验证时遇到了如上所述的问题,经过了几天时间才将问题解决。现在将解决该问题的解决方式分享一下。
首先我们阅读AliGenie开发者平台的文档,在接入方式及流程这一章节讲了第一步对接时平台发起的对接请求的例子如下:
https://xxx.com/auth/authorize?redirect_uri=https%3A%2F%2Fopen.bot.tmall.com%2Foauth%2Fcallback%3FskillId%3D11111111%26token%3DXXXXXXXXXX&client_id=XXXXXXXXX&response_type=code&state=111
参数说明:
redirect_uri: AliGenie平台的回调地址,该地址会加上AliGenie的参数进行urlEncode,所以合作方务必做好参数返回的兼容性
client_id: 在合作方上注册的应用Id
response_type: 响应方式为code
state:表示客户端的当前状态,可以指定任意值,认证服务器会原封不动地返回这个值。
大家可以发现对比OAuth2.0协议(OAuth2.0协议中文翻译文档链接:https://colobu.com/2017/04/28/oauth2-rfc6749/ )这里多了两个Get请求的参数skillId和token这两个参数,回顾某篇文章中的authorize.php这个文件(这里会加上我自己的一些注解):
require_once __DIR__.'/server.php';
$request = OAuth2\Request::createFromGlobals(); //这里获取了GET请求发过来的参数
$response = new OAuth2\Response();
// validate the authorize request
//获取GET请求中redirect_uri字段的参数这里获取到的应该是这种形式的参数
//https://open.bot.tmall.com/oauth/callback?skillId=42972&token=MTA1NDk5ODk2NEFGRUhJTkZEVlE=
//这里大家可以自行打印看一下
$m=$request->query['redirect_uri'];
//这里对$m参数进行了裁剪,裁剪后$temp为https://open.bot.tmall.com/oauth/callback
$temp = substr($m,0,strpos($m,"?"));
//将$temp参数重新赋值给$request->query['redirect_uri']这一字段
$request->query['redirect_uri']= $temp;
//上述的代码中作者的这些操作都是没有问题的,裁剪后将$temp参数重新赋值给
//$request->query['redirect_uri']主要目的是为了与数据库中储存的redirect_uri字段进行匹配,
//因为我们在数据库存的redirect_uri字段为https://open.bot.tmall.com/oauth/callback
//如果两者不相同的话将会通不过,if (!$server->validateAuthorizeRequest($request, $response))
//这段代码的验证。
#exit("$m||||$temp");
//验证GET传过来的参数是否有问题
if (!$server->validateAuthorizeRequest($request, $response)) {
$response->send();
die;
}
// display an authorization form
if (empty($_POST)) {
exit('
');
}
// print the authorization code if the user has authorized your client
$is_authorized = ($_POST['authorized'] === 'yes');
//处理请求并生成$response,大家在这个函数调用后可以打印一下$response,这个刚生成的$response是
//不符合AliGenie平台的要求的,AliGenie平台的要求可参考开发文档的《AliGenie智能家居接入流程》
//平台要求的返回格式如下,需要带上skillId和token这两个参数:
//https://open.bot.tmall.com/oauth/callback?skillId=1111111&token=XXXXXXXXXX&state=11&code=XXXXX
//而实际上我们这里得到的是https://open.bot.tmall.com/oauth/callback?state=11&code=XXXXX这种形式的
//所以作者在if判断中也做了处理
$server->handleAuthorizeRequest($request, $response, $is_authorized);
if ($is_authorized) {
// this is only here so that you get to see your code in the cURL request. Otherwise, we'd redirect back to the client
$code = substr($response->getHttpHeader('Location'), strpos($response->getHttpHeader('Location'), 'code=')+5);
$return = urldecode($m)."&code=".$code;
header("Location: ".$return); //重新设置$response中的Location:字段
//这里大家可以打印下$response只要是符合上述所说的AliGenie所述格式的就是没问题的
exit("SUCCESS! Authorization Code: $code");
}
$response->send(); //发送应答
?>
看了上面的注释,好像authorize.php文件没有问题,那么问题出在哪呢,大家可以在线测试中点击账户配置,然后在我们的服务器中查看Apache2服务器的运行日志,输入:
cat /var/log/apache2/access.log
然后观察日志中是否有访问token.php,如下图所示:
一般来说authorize.php正常的话,都会有对token.php的访问,只要出现对token.php的访问这说明,authorize.php这一步的访问是没有问题的。如果没有出现,那就检查authorize.php的答复是否符合要求,如果检查答复正常,但是还是没有日志中还是没有出现对token.php的访问,那出问题的可能性如下:
1、SSL证书不被AliGenie平台认可,这点我问了官方的人员说SSL证书不能是私有的(什么叫私有的SSL证书我还正不知道,希望知道的可以给我普及一下知识),一般来说免费申请的都可以用,我用的就是云平台免费申请的证书。
好了如果一切正常的话就出现我截图上面所示的log,看日志中token.php后面的返回码,上面的返回码是400说明是token.php这个文件出了问题。首先回顾一下某篇文章中的token.php这个文件:
require_once __DIR__.'/server.php';
//第一个错误就在这里,按照OAuth2协议访问令牌请求应该是POST方式,在GET中是没有数据的,这里不太明白作者这样做
//的原因,所以下面这一段代码可以删除
$_POST['grant_type']=$_GET['grant_type'];
$_POST['code']=$_GET['code'];
$_POST['redirect_uri']=$_GET['redirect_uri'];
$_POST['client_id']=$_GET['client_id'];
$_POST['client_secret']=$_GET['client_secret'];
#error_log($_POST['grant_type']);
#error_log($_POST['client_id']);
#error_log($_POST['client_secret']);
#error_log($_POST['code']);
#error_log($_POST['redirect_uri']);
// Handle a request for an OAuth2.0 Access Token and send the response to the client
$server = new OAuth2\Server($storage);
$server->addGrantType(new OAuth2\GrantType\AuthorizationCode($storage));
$server->handleTokenRequest(OAuth2\Request::createFromGlobals(), new OAuth2\Response())->send();
?>
上面这个代码不多说,不知道作者那边当时是怎么过的,这里就先贴一下我自己的token.php代码:
require_once __DIR__.'/server.php';
$tokenlog = fopen("tokenlog.txt","w"); //打开一个文件主要是为了储存log,可能会遇到权限不足的问题
//大家可以先创建一个tokenlog.txt的文件,然后输入sudo chmod 666 tokenlog.txt就可以解决
$request = OAuth2\Request::createFromGlobals(); //获取POST过来的数据
$m = $request->request['redirect_uri']; //POST过来的数据是储存在$request->request这个列表里的,
//GET的数据储存在$request->query这个列表里,这里获取redirect_uri这个数据段
fwrite($tokenlog,'$m = '.$m."\r\n"); //将$m写入文件
$temp = substr($m,0,strpos($m,"?")); //裁剪$m,这里这样做跟authorize.php中的理由一样,把skillId和token这两个参数裁剪掉
fwrite($tokenlog,'$temp = '.$temp."\r\n"); //将$temp写入文件
$request->request['redirect_uri'] = $temp; //将$temp重新赋值$request->request['redirect_uri'],为了与
//数据库中的redirect_uri字段保持一致,不一致会出错,这个是导致Oauth token返回不正确的主要原因
//将$request->request中的所有数据写入文件
foreach($request->request as $x => $x_value)
{
fwrite($tokenlog,$x ." = ".$x_value."\r\n");
}
fclose($tokenlog);
//处理请求并答复
$server->handleTokenRequest($request, new OAuth2\Response())->send();
?>
基本上Oauth token返回不正确这个原因是由redirect_uri字段与数据库中不相符导致的,大家在用我的代码的时候可以把文件这方面的代码删掉,这里加着主要是为了打印log调试比较方便。
希望上面所说的对大家有用,有遇到问题的也欢迎大家留言,在整个调试过程中用到的Postman工具比较多,对调试比较有帮助,如有错误也希望大家指正,谢谢!