網頁授權
關于 OAuth 2.0
OAuth
是一個關于授權(authorization
)的開放網絡標準,在全世界得到廣泛應用,目前的版本是 2.0
版。
OAuth
授權流程大致如下:
摘自:RFC 6749
步驟解釋:
(A)用戶打開客戶端以后,客戶端要求用戶給予授權。
(B)用戶同意給予客戶端授權。
(C)客戶端使用上一步獲得的授權,向認證服務器申請令牌。
(D)認證服務器對客戶端進行認證以后,確認無誤,同意發放令牌。
(E)客戶端使用令牌,向資源服務器申請獲取資源。
(F)資源服務器確認令牌無誤,同意向客戶端開放資源。
關于 OAuth
協議我們就簡單了解到這里,如果還有不熟悉的同學,請 Google
相關資料
微信 OAuth
在微信里的 OAuth
其實有兩種:公眾平臺網頁授權獲取用戶信息、開放平臺網頁登錄。
它們的區別有兩處,授權地址 不同,scope 不同。
-
公眾平臺網頁授權獲取用戶信息
- 授權 URL:https://open.weixin.qq.com/connect/oauth2/authorize
- Scopes:snsapi_base 與 snsapi_userinfo
-
開放平臺網頁登錄
- 授權 URL:https://open.weixin.qq.com/connect/qrconnect
- Scopes: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
)。
看懵了?沒事,使用 SDK
,你不用管這么多。
注意,上面的第 3
步:redirect_uri=callback.php
實際上我們在 Swoole
中用 redirect_uri=callback
回調地址,后面還會帶上授權目標頁面 user/profile
,所以完整的 redirect_uri
應該是下面的這樣的 PHP
去拼出來:'redirect_uri=' . urlencode('callback?target=user/profile')
,拼接結果為:redirect_uri=callback%3Ftarget%3Duser%2Fprofile
邏輯組成
從上面我們所描述的授權流程來看,我們至少有 3
個頁面:
- 業務頁面,也就是需要授權才能訪問的頁面。
- 發起授權頁,此頁面其實可以省略,可以做成一個中間件,全局檢查未登錄就發起授權。
-
授權回調頁,接收用戶授權后的狀態,并獲取用戶信息,寫入用戶會話狀態(
SESSION
)。
開始之前
在開始之前請一定要記住,先登錄公眾號后臺,找到 邊欄 “開發” 模塊下的 “接口權限”,點擊 “網頁授權獲取用戶基本信息” 后面的修改,添加你的網頁授權域名。
如果你的授權地址為:
http://www.abc.com/xxxxx
,那么請填寫www.abc.com
,也就是說請填寫與網址匹配的域名,前者如果填寫abc.com
是通過不了的。
SDK 中 OAuth 模塊的 API
在 SDK
中,我們使用名稱為 oauth
的模塊來完成授權服務,我們主要用到以下兩個 API
:
發起授權
// $redirectUrl 為跳轉目標,請自行 `302` 跳轉到目標地址
$redirectUrl = $officialAccount->oauth->scopes(['snsapi_userinfo'])
->redirect();
當然你也可以在發起授權的時候指定回調 URL
,比如設置回調 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
是一個字符串跳轉地址,請自行使用框架的跳轉方法實現跳轉,在 EasySwoole
中寫法為:
$this->response()->redirect($redirectUrl);
在原生 Swoole
中可以這樣寫:
// $response 為 \Swoole\Http\Response 的實例對象
$response->redirect($redirectUrl);
獲取已授權用戶
<?php
$code = "微信回調URL攜帶的 code";
$user = $officialAccount->oauth->userFromCode($code);
返回的 $user
是 EasySwoole\WeChat\OfficialAccount\OAuth\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
時的響應值
注意:
$user
里沒有openid
,$user->id
便是openid
。如果你想拿微信返回給你的原樣的全部信息,請使用:$user->getRaw()
;
當 scope
為 snsapi_base
時 $officialAccount->oauth->user();
對象里只有 id
,沒有其它信息。
網頁授權實例
我們這里來用 PHP
原生 Swoole
寫法舉個例子,oauth_callback
是我們的授權回調 URL
(未 urlencode
編碼的 URL
),user/profile
是我們需要授權才能訪問的頁面,它的 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;
}
// 已經登錄過
$user = $_SESSION['wechat_user'];
});
// ... 這里省略
授權回調頁:
// 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 授權結果用戶信息
$code = "微信回調URL攜帶的 code";
$user = $oauth->userFromCode($code);
$_SESSION['wechat_user'] = $user->toArray();
$targetUrl = empty($_SESSION['target_url']) ? '/' : $_SESSION['target_url'];
// 跳轉到 user/profile
$response->redirect($targetUrl);
});
// ... 這里省略
上面的例子呢都是基于 $_SESSION
來保持會話的,在微信客戶端中,你可以結合 Cookies
來存儲,但是有效期平臺不一樣時間也不一樣,好像 Android
的失效會快一些,不過基本也夠用了。