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