摘要:不太熟悉什么是的同學可以參考阮大神的文章,理解阮一峰授權碼模式授權代碼授予類型用于獲得訪問權限令牌和刷新令牌,并為機密客戶進行了優化。
OAuth 2.0
不太熟悉什么是OAuth2.0的同學可以參考阮大神的文章, 理解OAuth 2.0 - 阮一峰
授權碼模式(Authorization Code)# 授權代碼授予類型用于獲得訪問權限令牌和刷新令牌,并為機密客戶進行了優化。 # 由于這是一個基于重定向的流程,客戶端必須能夠與資源所有者的用戶代理(通常是Web)交互瀏覽器),能夠接收傳入請求(通過重定向)從授權服務器。 # 授權代碼流如下: +----------+ | Resource | | Owner | | | +----------+ ^ | (B) +----|-----+ Client Identifier +---------------+ | -+----(A)-- & Redirection URI ---->| | | User- | | Authorization | | Agent -+----(B)-- User authenticates --->| Server | | | | | | -+----(C)-- Authorization Code ---<| | +-|----|---+ +---------------+ | | ^ v (A) (C) | | | | | | ^ v | | +---------+ | | | |>---(D)-- Authorization Code ---------" | | Client | & Redirection URI | | | | | |<---(E)----- Access Token -------------------" +---------+ (w/ Optional Refresh Token)授權碼授權開發 引入OAuth-server包
# PHP 5.3.9+ composer require bshaffer/oauth2-server-php "^1.10"創建數據表
-- 你可使用相應的數據庫引擎:MySQL / SQLite / PostgreSQL / MS SQL Server -- 數據庫:oauth_test -- 細調過表相關結構,不過你也可以參考官方:http://bshaffer.github.io/oauth2-server-php-docs/cookbook/ DROP TABLE IF EXISTS `oauth_access_tokens`; CREATE TABLE `oauth_access_tokens` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `access_token` varchar(40) NOT NULL, `client_id` varchar(80) NOT NULL, `user_id` varchar(80) DEFAULT NULL, `expires` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `scope` text NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `IDX_ACCESS_TOKEN` (`access_token`) USING BTREE ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4; -- ---------------------------- -- Table structure for oauth_authorization_codes -- ---------------------------- DROP TABLE IF EXISTS `oauth_authorization_codes`; CREATE TABLE `oauth_authorization_codes` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `authorization_code` varchar(40) DEFAULT "", `client_id` varchar(80) DEFAULT "", `user_id` varchar(80) DEFAULT "0", `redirect_uri` varchar(2000) DEFAULT "", `expires` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `scope` text, `id_token` varchar(1000) DEFAULT "", PRIMARY KEY (`id`), UNIQUE KEY `IDX_CODE` (`authorization_code`) USING BTREE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- ---------------------------- -- Records of oauth_authorization_codes -- ---------------------------- -- ---------------------------- -- Table structure for oauth_clients -- ---------------------------- DROP TABLE IF EXISTS `oauth_clients`; CREATE TABLE `oauth_clients` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `client_id` varchar(80) DEFAULT "", `client_secret` varchar(80) DEFAULT "", `client_name` varchar(120) DEFAULT "", `redirect_uri` varchar(2000) DEFAULT "", `grant_types` varchar(80) DEFAULT "", `scope` varchar(4000) DEFAULT "", `user_id` varchar(80) DEFAULT "0", PRIMARY KEY (`id`), KEY `IDX_APP_SECRET` (`client_id`,`client_secret`) ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4; -- ---------------------------- -- Records of oauth_clients -- ---------------------------- INSERT INTO `oauth_clients` VALUES ("1", "testclient", "123456", "測試demo", "http://sxx.qkl.local/v2/oauth/cb", "authorization_code refresh_token", "basic get_user_info upload_pic", ""); -- ---------------------------- -- Table structure for oauth_jwt -- ---------------------------- DROP TABLE IF EXISTS `oauth_jwt`; CREATE TABLE `oauth_jwt` ( `client_id` varchar(80) NOT NULL, `subject` varchar(80) DEFAULT NULL, `public_key` varchar(2000) NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- ---------------------------- -- Records of oauth_jwt -- ---------------------------- -- ---------------------------- -- Table structure for oauth_public_keys -- ---------------------------- DROP TABLE IF EXISTS `oauth_public_keys`; CREATE TABLE `oauth_public_keys` ( `client_id` varchar(80) DEFAULT NULL, `public_key` varchar(2000) DEFAULT NULL, `private_key` varchar(2000) DEFAULT NULL, `encryption_algorithm` varchar(100) DEFAULT "RS256" ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- ---------------------------- -- Records of oauth_public_keys -- ---------------------------- -- ---------------------------- -- Table structure for oauth_refresh_tokens -- ---------------------------- DROP TABLE IF EXISTS `oauth_refresh_tokens`; CREATE TABLE `oauth_refresh_tokens` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `refresh_token` varchar(40) NOT NULL, `client_id` varchar(80) NOT NULL DEFAULT "", `user_id` varchar(80) DEFAULT "", `expires` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `scope` text, PRIMARY KEY (`id`), UNIQUE KEY `IDX_REFRESH_TOKEN` (`refresh_token`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4; -- ---------------------------- -- Table structure for oauth_scopes -- ---------------------------- DROP TABLE IF EXISTS `oauth_scopes`; CREATE TABLE `oauth_scopes` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `scope` varchar(80) NOT NULL DEFAULT "", `is_default` tinyint(1) unsigned NOT NULL DEFAULT "0", PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4; -- ---------------------------- -- Records of oauth_scopes -- ---------------------------- INSERT INTO `oauth_scopes` VALUES ("1", "basic", "1"); INSERT INTO `oauth_scopes` VALUES ("2", "get_user_info", "0"); INSERT INTO `oauth_scopes` VALUES ("3", "upload_pic", "0"); -- ---------------------------- -- Table structure for oauth_users 該表是Resource Owner Password Credentials Grant所使用 -- ---------------------------- DROP TABLE IF EXISTS `oauth_users`; CREATE TABLE `oauth_users` ( `uid` int(10) unsigned NOT NULL AUTO_INCREMENT, `username` varchar(80) DEFAULT "", `password` varchar(80) DEFAULT "", `first_name` varchar(80) DEFAULT "", `last_name` varchar(80) DEFAULT "", `email` varchar(80) DEFAULT "", `email_verified` tinyint(1) DEFAULT "0", `scope` text, PRIMARY KEY (`uid`) ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4; -- ---------------------------- -- Records of oauth_users -- ---------------------------- INSERT INTO `oauth_users` VALUES ("1", "qkl", "123456", "kl", "q", "", "", "");創建server
Authorization Server 角色
public function _initialize() { require_once dirname(APP_PATH) . "/vendor/autoload.php"; Autoloader::register(); } private function server() { $pdo = new PDO("mysql:host=ip;dbname=oauth_test", "user", "123456"); //創建存儲的方式 $storage = new OAuth2StoragePdo($pdo); //創建server $server = new OAuth2Server($storage); // 添加 Authorization Code 授予類型 $server->addGrantType(new OAuth2GrantTypeAuthorizationCode($storage)); return $server; }創建授權頁面(基于瀏覽器)
Authorization Server 角色
User Agent 角色,常規一般基于瀏覽器
// 授權頁面和授權 public function authorize() { // 該頁面請求地址類似: // http://sxx.qkl.local/v2/oauth/authorize?response_type=code&client_id=testclient&state=xyz&redirect_uri=http://sxx.qkl.local/v2/oauth/cb&scope=basic%20get_user_info%20upload_pic //獲取server對象 $server = $this->server(); $request = OAuth2Request::createFromGlobals(); $response = new OAuth2Response(); // 驗證 authorize request // 這里會驗證client_id,redirect_uri等參數和client是否有scope if (!$server->validateAuthorizeRequest($request, $response)) { $response->send(); die; } // 顯示授權登錄頁面 if (empty($_POST)) { //獲取client類型的storage //不過這里我們在server里設置了storage,其實都是一樣的storage->pdo.mysql $pdo = $server->getStorage("client"); //獲取oauth_clients表的對應的client應用的數據 $clientInfo = $pdo->getClientDetails($request->query("client_id")); $this->assign("clientInfo", $clientInfo); $this->display("authorize"); die(); } $is_authorized = true; // 當然這部分常規是基于自己現有的帳號系統驗證 if (!$uid = $this->checkLogin($request)) { $is_authorized = false; } // 這里是授權獲取code,并拼接Location地址返回相應 // Location的地址類似:http://sxx.qkl.local/v2/oauth/cb?code=69d78ea06b5ee41acbb9dfb90500823c8ac0241d&state=xyz // 這里的$uid不是上面oauth_users表的uid, 是自己系統里的帳號的id,你也可以省略該參數 $server->handleAuthorizeRequest($request, $response, $is_authorized, $uid); // if ($is_authorized) { // // 這里會創建Location跳轉,你可以直接獲取相關的跳轉url,用于debug // $code = substr($response->getHttpHeader("Location"), strpos($response->getHttpHeader("Location"), "code=")+5, 40); // exit("SUCCESS! Authorization Code: $code :: " . $response->getHttpHeader("Location")); // } $response->send(); } /** * 具體基于自己現有的帳號系統驗證 * @param $request * @return bool */ private function checkLogin($request) { //todo if ($request->request("username") != "qkl") { return $uid = 0; //login faile } return $uid = 1; //login success }創建獲取token
Authorization Server 角色
// 生成并獲取token public function token() { $server = $this->server(); $server->handleTokenRequest(OAuth2Request::createFromGlobals())->send(); exit(); }授權頁面
CLIENT 客戶端 角色
# 瀏覽器訪問: http://sxx.qkl.local/v2/oauth/authorize?response_type=code&client_id=testclient&state=xyz&redirect_uri=http://sxx.qkl.local/v2/oauth/cb&scope=basic%20get_user_info%20upload_pic授權頁面說明
# 我們換行分解下 http://sxx.qkl.local/v2/oauth/authorize? # response_type 固定寫死 code response_type=code& # client_id 我們oauth_clients表的client_id值 client_id=testclient& # state 自定義的參數,隨意字符串值 state=xyz& # redirect_uri 回調地址,這里最好是urlencode編碼,我這里演示沒編碼 # 注意這里的redirect_uri需要和oauth_clients表的redirect_uri字段做匹配處理 # redirect_uri字段可存取的方式: # 1. http://sxx.qkl.local/v2/oauth/cb # 2. http://sxx.qkl.local/v2/oauth/cb http://sxx.qkl.local/v2/oauth/cb2 ... 空格分割 redirect_uri=http://sxx.qkl.local/v2/oauth/cb& # response_type 固定寫死 code scope=basic%20get_user_info%20upload_pic客戶端獲取code并請求獲取access_token
CLIENT 客戶端 角色
// 客戶端回調,來自server端的Location跳轉到此 // 此處會攜帶上code和你自定義的state public function cb() { $request = OAuth2Request::createFromGlobals(); $url = "http://sxx.qkl.local/v2/oauth/token"; $data = [ "grant_type" => "authorization_code", "code" => $request->query("code"), "client_id" => "testclient", "client_secret" => "123456", "redirect_uri" => "http://sxx.qkl.local/v2/oauth/cb" ]; //todo 自定義的處理判斷 $state = $request->query("state"); $response = Curl::ihttp_post($url, $data); if (is_error($response)) { var_dump($response); } var_dump($response["content"]); }刷新token
Authorization Server 角色
// 創建刷新token的server private function refresh_token_server() { $pdo = new PDO("mysql:host=ip;dbname=oauth_test", "user", "123456"); $storage = new OAuth2StoragePdo($pdo); $config = [ "always_issue_new_refresh_token" => true, "refresh_token_lifetime" => 2419200, ]; $server = new OAuth2Server($storage, $config); // 添加一個 RefreshToken 的類型 $server->addGrantType(new OAuth2GrantTypeRefreshToken($storage, [ "always_issue_new_refresh_token" => true ])); // 添加一個token的Response $server->addResponseType(new OAuth2ResponseTypeAccessToken($storage, $storage, [ "refresh_token_lifetime" => 2419200, ])); return $server; } // 刷新token public function refresh_token() { $server = $this->refresh_token_server(); $server->handleTokenRequest(OAuth2Request::createFromGlobals())->send(); exit(); }客戶端請求refresh_token
CLIENT 客戶端 角色
// 客戶端模擬refresh_token public function client_refresh_token() { $request = OAuth2Request::createFromGlobals(); $url = "http://sxx.qkl.local/v2/oauth/refresh_token"; $data = [ "grant_type" => "refresh_token", "refresh_token" => "d9c5bee6a4ad7967ac044c99e40496aa2c3d28b4", "client_id" => "testclient", "client_secret" => "123456" ]; $response = Curl::ihttp_post($url, $data); if (is_error($response)) { var_dump($response); } var_dump($response["content"]); }scope授權資源
Authorization Server 角色
這里說明下 因為在上面表創建時,我創建了3個socpe[basic,get_user_info,upload_pic]用于測試
上面我們在瀏覽器訪問的授權地址上也填寫了三個權限,所以只要access_token正確在時效內,即可成功訪問
// 測試資源 public function res1() { $server = $this->server(); // Handle a request to a resource and authenticate the access token if (!$server->verifyResourceRequest(OAuth2Request::createFromGlobals())) { $server->getResponse()->send(); die; } $token = $server->getAccessTokenData(OAuth2Request::createFromGlobals()); $scopes = explode(" ", $token["scope"]); // todo 這里你可以寫成自己規則的scope驗證 if (!$this->checkScope("basic", $scopes)) { $this->ajaxReturn(["success" => false, "message" => "你沒有獲取該接口的scope"]); } $this->ajaxReturn(["success" => true, "message" => "你成功獲取該接口信息", "token"=>$token["user_id"]]); } // 用于演示檢測scope的方法 private function checkScope($myScope, $scopes) { return in_array($myScope, $scopes); }客戶端postman模擬測試
正確的access_token請求:
錯誤或失效的access_token請求:
Oauth2.0整體沒什么具體的技術含量,可以參照規范實現即可后續
PHP下的Oauth2.0嘗試 - OpenID Connect - 后續補位
附錄Oauth2.0 - Authorization Code Grant
使用Authorization_Code獲取Access_Token - QQ互聯接入
推薦閱讀登錄授權方案 - 網站的無密碼登錄 - 阮一峰
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/30882.html
摘要:基礎簡要而言是一種安全機制用于應用連接到身份認證服務器獲取用戶信息并將這些信息以安全可靠的方法返回給應用。這些信息被保存在身份認證服務器以確保特定的客戶端收到的信息只來自于合法的應用平臺。 OpenID Connect OpenID Connect簡介 OpenID Connect是基于OAuth 2.0規范族的可互操作的身份驗證協議。它使用簡單的REST / JSON消息流來實現,和...
摘要:原因使用簡單,可以很快上手,文檔齊全,功能完善。請求,端對應的模板里是告知用戶,即將授予的權限列表,以及是否允許授權的按鈕。請求,端獲取用戶資源各種授權類型,都可以很方便支持。 前奏 系統:Ubuntu 語言:PHP7 框架:YAF OAuth2.0:bshaffer/oauth2-server-php OAuth2.0 有很多開源代碼庫 Github 排名前兩位 thephple...
摘要:如果不使用回調地址桌面或手機程序,而是通過手動拷貝粘貼完成授權的話,那么只要攻擊者在在前面發起請求,就能拿到的。改進步驟傳遞參數而不是獲取已授權步驟申請時,必須傳遞讓參與簽名避免攻擊者假冒。 OAuth 流程與發展 (1.0 => 1.0a => 2.0) 概述 概述: 開放授權協議 作用: 允許第三方應用訪問服務提供方中注冊的終端用戶的部分資源 下面是官方描述: [OAuth描述]...
摘要:如果不使用回調地址桌面或手機程序,而是通過手動拷貝粘貼完成授權的話,那么只要攻擊者在在前面發起請求,就能拿到的。改進步驟傳遞參數而不是獲取已授權步驟申請時,必須傳遞讓參與簽名避免攻擊者假冒。 OAuth 流程與發展 (1.0 => 1.0a => 2.0) 概述 概述: 開放授權協議 作用: 允許第三方應用訪問服務提供方中注冊的終端用戶的部分資源 下面是官方描述: [OAuth描述]...
摘要:目前的版本是版,本文將對的一些基本概念和運行流程做一個簡要介紹。只要有一個第三方應用程序被破解,就會導致用戶密碼泄漏,以及所有被密碼保護的數據泄漏。運行流程客戶端向資源所有者請求授權。 OAuth(開放授權)是一個關于授權的開放標準,允許用戶讓第三方應用訪問該用戶在某一網站上存儲的私密的資源(如照片,視頻,聯系人列表),而無需將用戶名和密碼提供給第三方應用。目前的版本是2.0版,本文將...
閱讀 1635·2023-04-25 16:29
閱讀 957·2021-11-15 11:38
閱讀 2295·2021-09-23 11:45
閱讀 1424·2021-09-22 16:03
閱讀 2541·2019-08-30 15:54
閱讀 1204·2019-08-30 10:53
閱讀 2604·2019-08-29 15:24
閱讀 1104·2019-08-26 12:25