網(wǎng)頁授權(quán)
關(guān)于 OAuth 2.0
OAuth
是一個(gè)關(guān)于授權(quán)(authorization
)的開放網(wǎng)絡(luò)標(biāo)準(zhǔn),在全世界得到廣泛應(yīng)用,目前的版本是 2.0
版。
OAuth
授權(quán)流程大致如下:
摘自:RFC 6749
步驟解釋:
(A)用戶打開客戶端以后,客戶端要求用戶給予授權(quán)。
(B)用戶同意給予客戶端授權(quán)。
(C)客戶端使用上一步獲得的授權(quán),向認(rèn)證服務(wù)器申請(qǐng)令牌。
(D)認(rèn)證服務(wù)器對(duì)客戶端進(jìn)行認(rèn)證以后,確認(rèn)無誤,同意發(fā)放令牌。
(E)客戶端使用令牌,向資源服務(wù)器申請(qǐng)獲取資源。
(F)資源服務(wù)器確認(rèn)令牌無誤,同意向客戶端開放資源。
關(guān)于 OAuth
協(xié)議我們就簡單了解到這里,如果還有不熟悉的同學(xué),請(qǐng) Google
相關(guān)資料
微信 OAuth
在微信里的 OAuth
其實(shí)有兩種:公眾平臺(tái)網(wǎng)頁授權(quán)獲取用戶信息、開放平臺(tái)網(wǎng)頁登錄。
它們的區(qū)別有兩處,授權(quán)地址 不同,scope 不同。
-
公眾平臺(tái)網(wǎng)頁授權(quán)獲取用戶信息
- 授權(quán) URL:https://open.weixin.qq.com/connect/oauth2/authorize
- Scopes:snsapi_base 與 snsapi_userinfo
-
開放平臺(tái)網(wǎng)頁登錄
- 授權(quán) URL:https://open.weixin.qq.com/connect/qrconnect
- Scopes:snsapi_login
他們的邏輯都一樣:
- 用戶嘗試訪問一個(gè)我們的業(yè)務(wù)頁面,例如:
/user/profile
- 如果用戶已經(jīng)登錄,則正常顯示該頁面
- 系統(tǒng)檢查當(dāng)前訪問的用戶并未登錄(從
session
或者其它方式檢查),則跳轉(zhuǎn)到 微信授權(quán)服務(wù)器(上面的兩種中一種 授權(quán)URL
),并告知微信授權(quán)服務(wù)器我的 回調(diào) URL(redirect_uri=callback.php
),此時(shí)用戶看到藍(lán)色的授權(quán)確認(rèn)頁面(scope
為snsapi_base
時(shí)不顯示) - 用戶點(diǎn)擊確定完成授權(quán),瀏覽器跳轉(zhuǎn)到 回調(diào)URL:
callback.php
并帶上code
:?code=CODE&state=STATE
。 - 在
callback.php
中得到code
后,通過code
再次向微信服務(wù)器請(qǐng)求得到 網(wǎng)頁授權(quán) access_token 與openid
- 你可以選擇拿
openid
去請(qǐng)求API
得到用戶信息(可選) - 將用戶信息寫入
SESSION
。 - 跳轉(zhuǎn)到第
3
步寫入的target_url
頁面(/user/profile
)。
看懵了?沒事,使用 SDK
,你不用管這么多。
注意,上面的第 3
步:redirect_uri=callback.php
實(shí)際上我們?cè)?Swoole
中用 redirect_uri=callback
回調(diào)地址,后面還會(huì)帶上授權(quán)目標(biāo)頁面 user/profile
,所以完整的 redirect_uri
應(yīng)該是下面的這樣的 PHP
去拼出來:'redirect_uri=' . urlencode('callback?target=user/profile')
,拼接結(jié)果為:redirect_uri=callback%3Ftarget%3Duser%2Fprofile
邏輯組成
從上面我們所描述的授權(quán)流程來看,我們至少有 3
個(gè)頁面:
- 業(yè)務(wù)頁面,也就是需要授權(quán)才能訪問的頁面。
- 發(fā)起授權(quán)頁,此頁面其實(shí)可以省略,可以做成一個(gè)中間件,全局檢查未登錄就發(fā)起授權(quán)。
-
授權(quán)回調(diào)頁,接收用戶授權(quán)后的狀態(tài),并獲取用戶信息,寫入用戶會(huì)話狀態(tài)(
SESSION
)。
開始之前
在開始之前請(qǐng)一定要記住,先登錄公眾號(hào)后臺(tái),找到 邊欄 “開發(fā)” 模塊下的 “接口權(quán)限”,點(diǎn)擊 “網(wǎng)頁授權(quán)獲取用戶基本信息” 后面的修改,添加你的網(wǎng)頁授權(quán)域名。
如果你的授權(quán)地址為:
http://www.abc.com/xxxxx
,那么請(qǐng)?zhí)顚?www.abc.com
,也就是說請(qǐng)?zhí)顚懪c網(wǎng)址匹配的域名,前者如果填寫abc.com
是通過不了的。
SDK 中 OAuth 模塊的 API
在 SDK
中,我們使用名稱為 oauth
的模塊來完成授權(quán)服務(wù),我們主要用到以下兩個(gè) API
:
發(fā)起授權(quán)
// $redirectUrl 為跳轉(zhuǎn)目標(biāo),請(qǐng)自行 `302` 跳轉(zhuǎn)到目標(biāo)地址
$redirectUrl = $officialAccount->oauth->scopes(['snsapi_userinfo'])
->redirect();
當(dāng)然你也可以在發(fā)起授權(quán)的時(shí)候指定回調(diào) URL
,比如設(shè)置回調(diào) URL
為當(dāng)前頁面:
<?php
// 在 EasySwoole 中,$this->request() 為 EasySwoole 的請(qǐng)求對(duì)象
$redirectUrl = $officialAccount->oauth->scopes(['snsapi_userinfo'])
->redirect($this->request()->getUri());
// 在原生 Swoole 中,$request 為 \Swoole\Http\Request 的實(shí)例對(duì)象
$redirectUrl = $officialAccount->oauth->scopes(['snsapi_userinfo'])
->redirect($request->server['request_uri']);
它的返回值 $redirectUrl
是一個(gè)字符串跳轉(zhuǎn)地址,請(qǐng)自行使用框架的跳轉(zhuǎn)方法實(shí)現(xiàn)跳轉(zhuǎn),在 EasySwoole
中寫法為:
$this->response()->redirect($redirectUrl);
在原生 Swoole
中可以這樣寫:
// $response 為 \Swoole\Http\Response 的實(shí)例對(duì)象
$response->redirect($redirectUrl);
獲取已授權(quán)用戶
<?php
$code = "微信回調(diào)URL攜帶的 code";
$user = $officialAccount->oauth->userFromCode($code);
返回的 $user
是 EasySwoole\WeChat\OfficialAccount\OAuth\User
對(duì)象,你可以從該對(duì)象拿到更多的信息。
$user
可以用的方法:
-
$user->getId();
對(duì)應(yīng)微信的openid
-
$user->getNickname();
對(duì)應(yīng)微信的nickname
-
$user->getName();
對(duì)應(yīng)微信的nickname
-
$user->getAvatar();
頭像地址 -
$user->getRaw();
原始API
返回的結(jié)果 -
$user->getAccessToken();
access_token
-
$user->getRefreshToken();
refresh_token
-
$user->getExpiresIn();
expires_in
,access_token
的過期時(shí)間 -
$user->getTokenResponse();
返回access_token
時(shí)的響應(yīng)值
注意:
$user
里沒有openid
,$user->id
便是openid
。如果你想拿微信返回給你的原樣的全部信息,請(qǐng)使用:$user->getRaw()
;
當(dāng) scope
為 snsapi_base
時(shí) $officialAccount->oauth->user();
對(duì)象里只有 id
,沒有其它信息。
網(wǎng)頁授權(quán)實(shí)例
我們這里來用 PHP
原生 Swoole
寫法舉個(gè)例子,oauth_callback
是我們的授權(quán)回調(diào) URL
(未 urlencode
編碼的 URL
),user/profile
是我們需要授權(quán)才能訪問的頁面,它的 PHP
代碼如下:
// http://easyswoolewechat.com/user/profile
<?php
// ... 這里省略
$http->on('request', function (\Swoole\Http\Request $request, \Swoole\Http\Response $response) {
$config = [
// ...
'oauth' => [
'scopes' => ['snsapi_userinfo'],
'callback' => '/oauth_callback',
],
// ..
];
$officialAccount = \EasySwoole\WeChat\Factory::officialAccount($config);
$oauth = $officialAccount->oauth;
// 未登錄
if (empty($_SESSION['wechat_user'])) {
$_SESSION['target_url'] = 'user/profile';
$redirectUrl = $oauth->redirect();
$request->redirct($redirectUrl);
exit;
}
// 已經(jīng)登錄過
$user = $_SESSION['wechat_user'];
});
// ... 這里省略
授權(quán)回調(diào)頁:
// http://easyswoolewechat.com/oauth_callback
<?php
// ... 這里省略
$http->on('request', function (\Swoole\Http\Request $request, \Swoole\Http\Response $response) {
$config = [
// ...
];
$officialAccount = \EasySwoole\WeChat\Factory::officialAccount($config);
$oauth = $officialAccount->oauth;
// 獲取 OAuth 授權(quán)結(jié)果用戶信息
$code = "微信回調(diào)URL攜帶的 code";
$user = $oauth->userFromCode($code);
$_SESSION['wechat_user'] = $user->toArray();
$targetUrl = empty($_SESSION['target_url']) ? '/' : $_SESSION['target_url'];
// 跳轉(zhuǎn)到 user/profile
$response->redirect($targetUrl);
});
// ... 這里省略
上面的例子呢都是基于 $_SESSION
來保持會(huì)話的,在微信客戶端中,你可以結(jié)合 Cookies
來存儲(chǔ),但是有效期平臺(tái)不一樣時(shí)間也不一樣,好像 Android
的失效會(huì)快一些,不過基本也夠用了。