從0構(gòu)建Oauth2Server服務(wù)?之Token?編解碼
Token 編解碼
令牌提供了一種通過在令牌字符串本身中編碼所有必要信息來避免將令牌存儲在數(shù)據(jù)庫中的方法。這樣做的主要好處是 API 服務(wù)器能夠驗證訪問令牌,而無需對每個 API 請求進(jìn)行數(shù)據(jù)庫查找,從而使 API 更容易擴(kuò)展。
OAuth 2.0 Bearer Tokens 的好處是應(yīng)用程序不需要知道您決定如何在您的服務(wù)中實現(xiàn)訪問令牌。這意味著以后可以在不影響客戶端的情況下更改您的實現(xiàn)。
如果您已經(jīng)擁有一個可水平擴(kuò)展的分布式數(shù)據(jù)庫系統(tǒng),那么您可能無法通過使用自編碼令牌獲得任何好處。事實上,如果您已經(jīng)解決了分布式數(shù)據(jù)庫問題,則使用自編碼令牌只會引入新問題,因為使自編碼令牌無效成為一個額外的障礙。
有很多方法可以對令牌進(jìn)行自編碼。您選擇的實際方法只對您的實施很重要,因為令牌信息不會暴露給外部開發(fā)人員。
實現(xiàn)自編碼令牌的最常見方法是使用 JWS 規(guī)范,創(chuàng)建要包含在令牌中的所有數(shù)據(jù)的 JSON 序列化表示,并使用只有授權(quán)服務(wù)器知道的私鑰對生成的字符串進(jìn)行簽名.
JWT 訪問令牌編碼
下面的代碼是用 PHP 編寫的,并使用Firebase PHP-JWT庫來編碼和驗證令牌。您需要包含該庫才能運(yùn)行示例代碼實際上,授權(quán)服務(wù)器將有一個用于簽署令牌的私鑰,資源服務(wù)器將從授權(quán)服務(wù)器元數(shù)據(jù)中獲取公鑰以用于驗證令牌。在這個例子中,我們每次都生成一個新的私鑰,并在同一個腳本中驗證令牌。實際上,您需要將私鑰存儲在某處以使用相同的密鑰一致地簽署令牌。
<?php use \Firebase\JWT\JWT; # Generate a private key to sign the token. # The public key would need to be published at the authorization # server if a separate resource server needs to validate the JWT $private_key = openssl_pkey_new([ 'digest_alg' => 'sha256', 'private_key_bits' => 1024, 'private_key_type' => OPENSSL_KEYTYPE_RSA ]); # Set the user ID of the user this token is for $user_id = "1000"; # Set the client ID of the app that is generating this token $client_id = 'https://example-app.com'; # Provide the list of scopes this token is valid for $scope = 'read write'; $token_data = array( # Issuer (the authorization server identifier) 'iss' => 'https://' . $_SERVER['PHP_SELF'], # Expires At 'exp' => time()+7200, // Valid for 2 hours # Audience (The identifier of the resource server) 'aud' => 'api://default', # Subject (The user ID) 'sub' => $user_id, # Client ID 'client_id' => $client_id, # Issued At 'iat' => time(), # Identifier of this token 'jti' => microtime(true).'.'.bin2hex(random_bytes(10)), # The list of OAuth scopes this token includes 'scope' => $scope ); $token_string = JWT::encode($token_data, $private_key, 'RS256');
這將產(chǎn)生一個字符串,例如:
eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJodH
RwczovL2F1dGhvcml6YXRpb24tc2VydmVyLmNvbS8iLCJleHAiO
jE2MzczNDQ1NzIsImF1ZCI6ImFwaTovL2RlZmF1bHQiLCJzdWIi
OiIxMDAwIiwiY2xpZW50X2lkIjoiaHR0cHM6Ly9leGFtcGxlLWF
wcC5jb20iLCJpYXQiOjE2MzczMzczNzIsImp0aSI6IjE2MzczMz
czNzIuMjA1MS42MjBmNWEzZGMwZWJhYTA5NzMxMiIsInNjb3BlI
joicmVhZCB3cml0ZSJ9.SKDO_Gu96WeHkR_Tv0d8gFQN1SEdpN8
S_h0IJQyl_5syvpIRA5wno0VDFi34k5jbnaY5WHn6Y912IOmg6t
MO91KlYOU1MNdVhHUoPoNUzYtl_nNab7Ywe29kxgrekm-67ZInD
I8RHbSkL7Z_N9eZz_J8c3EolcsoIf-Dd5n9y_Y
該令牌由三個部分組成,以句點(diǎn)分隔。第一部分描述了使用的簽名方法。第二部分包含令牌數(shù)據(jù)。第三部分是簽名。
例如,此令牌的第一個組件是此 JSON 對象:
{ "typ":"JWT", "alg":"RS256" }
第二個組件包含 API 端點(diǎn)處理請求所需的實際數(shù)據(jù),例如用戶標(biāo)識和范圍訪問。
{ "iss": "https://authorization-server.com/", "exp": 1637344572, "aud": "api://default", "sub": "1000", "client_id": "https://example-app.com", "iat": 1637337372, "jti": "1637337372.2051.620f5a3dc0ebaa097312", "scope": "read write" }
然后對這兩個部分進(jìn)行 base64 編碼,JWT 庫計算這兩個字符串的 RS256 簽名,然后用句點(diǎn)連接所有三個部分。
解碼
可以使用相同的 JWT 庫驗證訪問令牌。該庫將同時對簽名進(jìn)行解碼和驗證,如果簽名無效或令牌的到期日期已過,則拋出異常。
您需要與簽署令牌的私鑰相對應(yīng)的公鑰。通常,您可以從授權(quán)服務(wù)器的元數(shù)據(jù)文檔中獲取它,但在本例中,我們將從之前生成的私鑰中派生出公鑰。
注意:任何人都可以通過對令牌字符串的中間部分進(jìn)行base64解碼來讀取令牌信息。因此,不要在令牌中存儲私人信息或您不希望用戶或開發(fā)人員看到的信息,這一點(diǎn)很重要。如果想隱藏token信息,可以使用JSON Web Encryption spec對token中的數(shù)據(jù)進(jìn)行加密。
$public_key = openssl_pkey_get_details($private_key)['key']; try { # Note: You must provide the list of supported algorithms in order to prevent # an attacker from bypassing the signature verification. See: # https://auth0.com/blog/critical-vulnerabilities-in-json-web-token-libraries/ $token = JWT::decode($token_string, $jwt_key, ['RS256']); $error = false; } catch(\Firebase\JWT\ExpiredException $e) { $token = false; $error = 'expired'; $error_description = 'The token has expired'; } catch(\Firebase\JWT\SignatureInvalidException $e) { $token = false; $error = 'invalid'; $error_description = 'The token provided was malformed'; } catch(Exception $e) { $token = false; $error = 'unauthorized'; $error_description = $e->getMessage(); } if($error) { header('HTTP/1.1 401 Unauthorized'); echo json_encode(array( 'error'=>$error, 'error_description'=>$error_description )); die(); } else { // Now $token has all the data that we encoded in it originally print_r($token); }
Invalidating
因為令牌可以在不進(jìn)行數(shù)據(jù)庫查找的情況下進(jìn)行驗證,所以在令牌過期之前無法使其失效。您需要采取額外的步驟來使自編碼的令牌無效,例如臨時存儲已撤銷令牌的列表,這是令jti
牌中聲明的一種用途。有關(guān)詳細(xì)信息,請參閱刷新訪問令牌。
以上就是從0構(gòu)建Oauth2Server服務(wù) 之Token 編解碼的詳細(xì)內(nèi)容,更多關(guān)于Oauth2Server Token編解碼的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
解析php dirname()與__FILE__常量的應(yīng)用
本篇文章是對php中的dirname()與__FILE__常量的應(yīng)用進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-06-06PHP基于文件鎖解決多進(jìn)程同時讀寫一個文件問題示例
這篇文章主要介紹了PHP基于文件鎖解決多進(jìn)程同時讀寫一個文件的方法,結(jié)合實例形式分析了PHP使用flock進(jìn)行文件讀寫加鎖操作用法,需要的朋友可以參考下2017-09-09PHP計劃任務(wù)之關(guān)閉瀏覽器后仍然繼續(xù)執(zhí)行的函數(shù)
通過使用ignore_user_abort函數(shù)的特性,實現(xiàn)PHP計劃任務(wù),需要的朋友可以參考下。2010-07-07php+ajax登錄跳轉(zhuǎn)登錄實現(xiàn)思路
這篇文章主要介紹了php+ajax登錄跳轉(zhuǎn)登錄實現(xiàn)思路,非常的簡單,有需要的小伙伴可以參考下2016-07-07關(guān)于php連接mssql:pdo odbc sql server
研究了很久,終于發(fā)現(xiàn):最新的php 5.3.6中php_mssql.dll,php_pdo_mssql.dll都已經(jīng)不見了。2011-07-07