OAuth是用于服務端與客戶端授權登錄的協議,OAuth2.0是OAuth的第二個版本,關于OAuth2.0的基礎知識,可以閱讀阮一峰的一篇博文,對OAuth2.0的介紹非常詳細,只要理解了OAuth2.0的授權過程,在自己網站實現OAuth2.0并不復雜。
本文將講解如何基于thinkphp5.1的框架實現OAuth2.0的服務端。
1 環境搭建
首先確保你已經搭建好了服務器,并且已經能夠正常訪問你的服務器。我的環境Xampp+thinkphp5.1.
2 安裝OAuth2.0 php包
你頁根據OAuth2.0的協議自己去實現代碼,但是最快捷最安全最可靠的方法當然是移植第三方OAuth2.0包。OAuth官網提供了很多第三方包,詳見網站https://oauth.net/code/, 如下圖,因為thinkphp是基于php語言,因此我選擇了PHP下第一個。
點擊PHP OAuth2 Server會跳入源碼下載庫,將其下載到電腦即可。
下載后解壓,我們只需要將里面/src/OAuth文件夾整個拷貝到tp5/extend/目錄下,就可以自動注冊對應的命名空間。之后我們就可以使用\OAuth2\...的方式去使用OAuth里面的任何方法。
3 實現OAuth服務端
3.1 創建數據庫
由于我們之前下載的OAuth包有用到很多數據表,所以需要按照其要求創建好數據表,創建代碼如下:
CREATE TABLE oauth_clients (
? client_id? ? ? ? ? ? VARCHAR(80)? NOT NULL,
? client_secret? ? ? ? VARCHAR(80),
? redirect_uri? ? ? ? ? VARCHAR(2000),
? grant_types? ? ? ? ? VARCHAR(80),
? scope? ? ? ? ? ? ? ? VARCHAR(4000),
? user_id? ? ? ? ? ? ? VARCHAR(80),
? PRIMARY KEY (client_id)
);
CREATE TABLE oauth_access_tokens (
? access_token? ? ? ? VARCHAR(40)? ? NOT NULL,
? client_id? ? ? ? ? ? VARCHAR(80)? ? NOT NULL,
? user_id? ? ? ? ? ? ? VARCHAR(80),
? expires? ? ? ? ? ? ? TIMESTAMP? ? ? NOT NULL,
? scope? ? ? ? ? ? ? ? VARCHAR(4000),
? PRIMARY KEY (access_token)
);
CREATE TABLE oauth_authorization_codes (
? authorization_code? VARCHAR(40)? ? NOT NULL,
? client_id? ? ? ? ? VARCHAR(80)? ? NOT NULL,
? user_id? ? ? ? ? ? VARCHAR(80),
? redirect_uri? ? ? ? VARCHAR(2000),
? expires? ? ? ? ? ? TIMESTAMP? ? ? NOT NULL,
? scope? ? ? ? ? ? ? VARCHAR(4000),
? id_token? ? ? ? ? ? VARCHAR(1000),
? PRIMARY KEY (authorization_code)
);
CREATE TABLE oauth_refresh_tokens (
? refresh_token? ? ? VARCHAR(40)? ? NOT NULL,
? client_id? ? ? ? ? VARCHAR(80)? ? NOT NULL,
? user_id? ? ? ? ? ? VARCHAR(80),
? expires? ? ? ? ? ? TIMESTAMP? ? ? NOT NULL,
? scope? ? ? ? ? ? ? VARCHAR(4000),
? PRIMARY KEY (refresh_token)
);
CREATE TABLE oauth_users (
? username? ? ? ? ? ? VARCHAR(80),
? password? ? ? ? ? ? VARCHAR(80),
? first_name? ? ? ? ? VARCHAR(80),
? last_name? ? ? ? ? VARCHAR(80),
? email? ? ? ? ? ? ? VARCHAR(80),
? email_verified? ? ? BOOLEAN,
? scope? ? ? ? ? ? ? VARCHAR(4000)
);
CREATE TABLE oauth_scopes (
? scope? ? ? ? ? ? ? VARCHAR(80)? ? NOT NULL,
? is_default? ? ? ? ? BOOLEAN,
? PRIMARY KEY (scope)
);
CREATE TABLE oauth_jwt (
? client_id? ? ? ? ? VARCHAR(80)? ? NOT NULL,
? subject? ? ? ? ? ? VARCHAR(80),
? public_key? ? ? ? ? VARCHAR(2000)? NOT NULL
);
3.2 創建控制器
需要在tp5/application/index/controller下創建一個控制器,命名為OAuth.php,寫入以下代碼,控制器就創建完成了。
<?php
namespace app\index\controller;
class OAuth extends \think\Controller
{
}
3.3 實現authorize
OAuth 2.0的運行流程如下圖。
所以第一步是實現authorization。
我們在之前創建好的控制器中添加一個函數authorize()
代碼如下(注意,dbname需要換成你自己的數據庫的名字,下同):
<?php
namespace app\index\controller;
class OAuth extends \think\Controller
{
? ? public function authorize()
? ? {
? ? ? ? global $server;
? ? ? ? $dsn? ? ? = 'mysql:dbname=XXX;host=127.0.0.1';
? ? ? ? $username = 'root';
? ? ? ? $password = '';
? ? ? ? \OAuth2\Autoloader::register();
? ? ? ? // $dsn is the Data Source Name for your database, for exmaple "mysql:dbname=my_oauth2_db;host=localhost"
? ? ? ? $storage = new \OAuth2\Storage\Pdo(array('dsn' => $dsn, 'username' => $username, 'password' => $password));
? ? ? ? // Pass a storage object or array of storage objects to the OAuth2 server class
? ? ? ? $server = new \OAuth2\Server($storage);
? ? ? ? // Add the "Client Credentials" grant type (it is the simplest of the grant types)
? ? ? ? $server->addGrantType(new \OAuth2\GrantType\ClientCredentials($storage));
? ? ? ? // Add the "Authorization Code" grant type (this is where the oauth magic happens)
? ? ? ? $server->addGrantType(new \OAuth2\GrantType\AuthorizationCode($storage));
? ? ? ? $request = \OAuth2\Request::createFromGlobals();
? ? ? ? $response = new \OAuth2\Response();
? ? ? ? // validate the authorize request
? ? ? ? if (!$server->validateAuthorizeRequest($request, $response)) {
? ? ? ? ? ? die;
? ? ? ? }
? ? ? ? // display an authorization form
? ? ? ? if (empty($_POST)) {
? ? ? ? ? exit('
? ? ? ? <form method="post">
? ? ? ? ? <label>Do You Authorize TestClient?</label><br />
? ? ? ? ? <input type="submit" name="authorized" value="yes">
? ? ? ? ? <input type="submit" name="authorized" value="no">
? ? ? ? </form>');
? ? ? ? }
? ? ? ? // print the authorization code if the user has authorized your client
? ? ? ? $is_authorized = ($_POST['authorized'] === 'yes');
? ? ? ? $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, 40);
? ? ? ? ? exit("SUCCESS! Authorization Code: $code");
? ? ? ? }
? ? ? ? $response->send();
? ? }
}
在tp5/route/route.php中創建相應路由,post方法和get方法都創建
Route::get('authorize', 'OAuth/authorize');
Route::post('authorize', 'OAuth/authorize');
接下來驗證創建的authorize是否成功,通過以下鏈接去訪問,在瀏覽器中輸入以下鏈接,回車后就會顯示一個驗證表單,當你點擊yes按鈕后,如果窗口顯示一串字符,那么就表示authorize創建成功了,這串字符就是code,接下來需要通過這個code去獲取token。
http://localhost/authorize.php?response_type=code&client_id=testclient&state=xyz
3.4 實現token申請方法
在OAuth.php控制器中添加函數token(),代碼如下
public function token(){
? ? ? ? global $server;
? ? ? ? $dsn? ? ? = 'mysql:dbname=XXX;host=127.0.0.1';
? ? ? ? $username = 'root';
? ? ? ? $password = '';
? ? ? ? \OAuth2\Autoloader::register();
? ? ? ? // $dsn is the Data Source Name for your database, for exmaple "mysql:dbname=my_oauth2_db;host=localhost"
? ? ? ? $storage = new \OAuth2\Storage\Pdo(array('dsn' => $dsn, 'username' => $username, 'password' => $password));
? ? ? ? // Pass a storage object or array of storage objects to the OAuth2 server class
? ? ? ? $server = new \OAuth2\Server($storage);
? ? ? ? // Add the "Client Credentials" grant type (it is the simplest of the grant types)
? ? ? ? $server->addGrantType(new \OAuth2\GrantType\ClientCredentials($storage));
? ? ? ? // Add the "Authorization Code" grant type (this is where the oauth magic happens)
? ? ? ? $server->addGrantType(new \OAuth2\GrantType\AuthorizationCode($storage));
? ? ? ? // Handle a request for an OAuth2.0 Access Token and send the response to the client
? ? ? ? $server->handleTokenRequest(\OAuth2\Request::createFromGlobals())->send();?
? ? }
在tp5/route/route.php中創建相應路由,post方法和get方法都創建
Route::get('token', 'OAuth/token');
Route::post('token', 'OAuth/token');
在測試是否獲取token之前,我們需要在oauth_clients表中加一條數據,可執行如下SQL:
INSERT INTO oauth_clients (client_id, client_secret, redirect_uri) VALUES ("testclient", "testpass", "http://fake/");
接下來從CMD運行以下內容,注意:code的值需要換成你上一步生成的code
curl -u testclient:testpass http://localhost/token.php -d 'grant_type=authorization_code&code=YOUR_CODE'
如果成功的話,你應該會得到access token,如下內容
{"access_token":"6f05ad622a3d32a5a81aee5d73a5826adb8cbf63","expires_in":3600,"token_type":"bearer","scope":null}
3.5 實現Resource獲取
在OAuth.php控制器中添加函數resource(),代碼如下
public function resource()
? ? {
? ? ? ? // include our OAuth2 Server object
? ? ? ? global $server;
? ? ? ? $dsn? ? ? = 'mysql:dbname=XXX;host=127.0.0.1';
? ? ? ? $username = 'root';
? ? ? ? $password = '';
? ? ? ? \OAuth2\Autoloader::register();
? ? ? ? // $dsn is the Data Source Name for your database, for exmaple "mysql:dbname=my_oauth2_db;host=localhost"
? ? ? ? $storage = new \OAuth2\Storage\Pdo(array('dsn' => $dsn, 'username' => $username, 'password' => $password));
? ? ? ? // Pass a storage object or array of storage objects to the OAuth2 server class
? ? ? ? $server = new \OAuth2\Server($storage);
? ? ? ? // Add the "Client Credentials" grant type (it is the simplest of the grant types)
? ? ? ? $server->addGrantType(new \OAuth2\GrantType\ClientCredentials($storage));
? ? ? ? // Add the "Authorization Code" grant type (this is where the oauth magic happens)
? ? ? ? $server->addGrantType(new \OAuth2\GrantType\AuthorizationCode($storage));
? ? ? ? // Handle a request to a resource and authenticate the access token
? ? ? ? if (!$server->verifyResourceRequest(\OAuth2\Request::createFromGlobals())) {
? ? ? ? ? ? $server->getResponse()->send();
? ? ? ? ? ? die;
? ? ? ? }
? ? ? ? echo json_encode(array('success' => true, 'message' => 'You accessed my APIs!'));
? ? }
在tp5/route/route.php中創建相應路由,post方法和get方法都創建
Route::get('resource', 'OAuth/resource');
Route::post('resource', 'OAuth/resource');
驗證:通過CMD運行以下內容(將access token的值換成上一次獲取的access token):
curl http://localhost/resource.php -d 'access_token=YOUR_TOKEN'
如果成功,將會獲得以下響應:
{"success":true,"message":"You accessed my APIs!"}
4 總結
至此,OAuth所有相關的都實現了,整個過程就是客戶端去想服務端申請token,然后拿著這個token向服務端獲取資源的過程。后續有什么不明白的地方,大家可以在下面評論,我有時間會回答大家。關于oauth2-server-php庫的更多詳情,大家可以訪問http://bshaffer.github.io/oauth2-server-php-docs/。