网页授权
关于 OAuth2.0
OAuth是一个关于授权(authorization)的开放网络标准,在全世界得到广泛应用,目前的版本是2.0版。

摘自:RFC 6749
步骤解释:
(A)用户打开客户端以后,客户端要求用户给予授权。(B)用户同意给予客户端授权。(C)客户端使用上一步获得的授权,向认证服务器申请令牌。(D)认证服务器对客户端进行认证以后,确认无误,同意发放令牌。(E)客户端使用令牌,向资源服务器申请获取资源。(F)资源服务器确认令牌无误,同意向客户端开放资源。
关于 OAuth 协议我们就简单了解到这里,如果还有不熟悉的同学,请 Google 相关资料
微信 OAuth
在微信里的 OAuth 其实有两种:公众平台网页授权获取用户信息、开放平台网页登录。
它们的区别有两处,授权地址不同,scope 不同。
公众平台网页授权获取用户信息 授权 URL:
https://open.weixin.qq.com/connect/oauth2/authorizeScopes:snsapi_base与snsapi_userinfo开放平台网页登录 授权 URL:
https://open.weixin.qq.com/connect/qrconnectScopes:snsapi_login
他们的逻辑都一样:
- 用户尝试访问一个我们的业务页面,例如:
/user/profile - 如果用户已经登录,则正常显示该页面
- 系统检查当前访问的用户并未登录(从 session 或者其它方式检查),则跳转到跳转到微信授权服务器(上面的两种中一种授权 URL ),并告知微信授权服务器我的回调URL(redirect_uri=callback.php),此时用户看到蓝色的授权确认页面(
scope为snsapi_base时不显示) - 用户点击确定完成授权,浏览器跳转到回调URL:
callback.php并带上code:?code=CODE&state=STATE。 - 在
callback.php中得到code后,通过code再次向微信服务器请求得到 网页授权 access_token 与openid - 你可以选择拿
openid去请求 API 得到用户信息(可选) - 将用户信息写入 SESSION。
- 跳转到第 3 步写入的
target_url页面(/user/profile)。
{warning} 看懵了?没事,使用 SDK,你不用管这么多。:smile:
注意,上面的第3步:redirect_uri=callback.php实际上我们会在
callback.php后面还会带上授权目标页面user/profile,所以完整的redirect_uri应该是下面的这样的PHP去拼出来:'redirect_uri='.urlencode('callback.php?target=user/profile')结果:redirect_uri=callback.php%3Ftarget%3Duser%2Fprofile
逻辑组成
从上面我们所描述的授权流程来看,我们至少有3个页面:
- 业务页面,也就是需要授权才能访问的页面。
- 发起授权页,此页面其实可以省略,可以做成一个中间件,全局检查未登录就发起授权。
- 授权回调页,接收用户授权后的状态,并获取用户信息,写入用户会话状态(SESSION)。
开始之前
在开始之前请一定要记住,先登录公众号后台,找到边栏 “开发” 模块下的 “接口权限”,点击 “网页授权获取用户基本信息” 后面的修改,添加你的网页授权域名。
如果你的授权地址为:
http://www.abc.com/xxxxx,那么请填写www.abc.com,也就是说请填写与网址匹配的域名,前者如果填写abc.com是通过不了的。
SDK 中 OAuth 模块的 API
在 SDK 中,我们使用名称为 oauth 的模块来完成授权服务,我们主要用到以下两个 API:
发起授权
// $redirectUrl 为跳转目标,请自行 302 跳转到目标地址$redirectUrl = $app->oauth->scopes(['snsapi_userinfo'])->redirect();
当然你也可以在发起授权的时候指定回调URL,比如设置回调URL为当前页面:
$redirectUrl = $app->oauth->scopes(['snsapi_userinfo'])->redirect($request->fullUrl());
它的返回值 $redirectUrl 是一个字符串跳转地址,请自行使用框架的跳转方法实现跳转,PHP 原生写法:
header("Location: {$redirectUrl}");
在 Laravel 框架中控制器方法是要求返回响应值的,那么你就直接:
return \redirect($redirectUrl);
获取已授权用户
$code = "微信回调URL携带的 code";$user = $app->oauth->userFromCode($code);
返回的 $user 是 Overtrue\Socialite\User 对象,你可以从该对象拿到更多的信息。
$user 可以用的方法:
$user->getId();对应微信的openid$user->getNickname();对应微信的nickname$user->getName();对应微信的nickname$user->getAvatar();头像地址$user->getRaw();原始 API 返回的结果$user->getAccessToken();access_token$user->getRefreshToken();refresh_token$user->getExpiresIn();expires_in,Access Token 过期时间$user->getTokenResponse();返回access_token时的响应值
{warning} 注意:
$user里没有openid,$user->id便是openid. 如果你想拿微信返回给你的原样的全部信息,请使用:$user->getRaw();
当 scope 为 snsapi_base 时 $oauth->user(); 对象里只有 id,没有其它信息。
网页授权实例
我们这里来用原生 PHP 写法举个例子,oauth_callback 是我们的授权回调URL (未urlencode编码的URL), user/profile 是我们需要授权才能访问的页面,它的 PHP 代码如下:
// http://easywechat.org/user/profile<?phpuse EasyWeChat\Factory;$config = [// ...'oauth' => ['scopes' => ['snsapi_userinfo'],'callback' => '/oauth_callback',],// ..];$app = Factory::officialAccount($config);$oauth = $app->oauth;// 未登录if (empty($_SESSION['wechat_user'])) {$_SESSION['target_url'] = 'user/profile';$redirectUrl = $oauth->redirect();header("Location: {$redirectUrl}");exit;}// 已经登录过$user = $_SESSION['wechat_user'];// ...
授权回调页:
// http://easywechat.org/oauth_callback<?phpuse EasyWeChat\Factory;$config = [// ...];$app = Factory::officialAccount($config);$oauth = $app->oauth;// 获取 OAuth 授权结果用户信息$code = "微信回调URL携带的 code";$user = $oauth->userFromCode();$_SESSION['wechat_user'] = $user->toArray();$targetUrl = empty($_SESSION['target_url']) ? '/' : $_SESSION['target_url'];header('Location:'. $targetUrl); // 跳转到 user/profile
上面的例子呢都是基于 $_SESSION 来保持会话的,在微信客户端中,你可以结合 Cookies 来存储,但是有效期平台不一样时间也不一样,好像 Android 的失效会快一些,不过基本也够用了。
参考阅读
- 本模块基于 overtrue/socialite 实现,更多的使用请阅读该扩展包文档。
- state 参数的使用: overtrue/socialite/#state
