智威湯遜訪問令牌
概觀
JWT訪問令牌提供了一種建立和驗證訪問令牌的方法,而不須要像數據庫這樣的中央存儲。這在驗證訪問令牌時減小了 OAuth2服務的延遲。
JWT訪問令牌使用JSON Web簽名 (第6.2章)和 公鑰加密 來肯定其有效性。OAuth2.0服務器使用a標記令牌private key
,其餘方可使用服務器驗證令牌public key
。
格式
JWT訪問令牌具備如下格式:
HEADER.PAYLOAD.SIGNATURE
這HEADER
是如下JSON的Base64 URL安全編碼:
{"typ": "JWT", "alg":"RS256"}
這PAYLOAD
是一個帶有如下字段的JSON對象的Base64 URL安全編碼:
{
"id": "394a71988caa6cc30601e43f5b6569d52cd7f6df", "jti": "394a71988caa6cc30601e43f5b6569d52cd7f6df", "iss": "issuer_id", "aud": "client_id", "sub": "user_id", "exp": 1483711650, "iat": 1483708050, "token_type": "bearer", "scope": "onescope twoscope" }
id
- 令牌的內部標識jti
- 令牌的惟一令牌標識符(JWT ID)iss
- 頒發令牌的服務器的ID(頒發者)aud
- 請求令牌的客戶的身份(受衆)sub
- 令牌被釋放的用戶的標識(主題)exp
- 令牌到期時的UNIX時間戳(到期)iat
- 建立令牌時的UNIX時間戳(發出時間)token_type
- 這種象徵,將成爲持有者scope
- 發佈令牌的空間分隔的做用域列表
使用JWT訪問令牌與此庫
建立公鑰和私鑰對
要開始,你須要一個公鑰/私鑰對。這些可使用如下命令在任何基於Unix的操做系統上生成:
# private key $ openssl genrsa -out privkey.pem 2048 # public key $ openssl rsa -in privkey.pem -pubout -out pubkey.pem
基本用法
配置服務器的最簡單方法是爲use_jwt_access_tokens
OAuth服務器的配置提供選項:
$server = new OAuth2\Server($storage, array( 'use_jwt_access_tokens' => true, ));
這將須要您建立一個PublicKey
存儲對象。您可使用內置Memory
存儲:
// your public key strings can be passed in however you like $publicKey = file_get_contents('/path/to/pubkey.pem'); $privateKey = file_get_contents('/path/to/privkey.pem'); // create storage $storage = new OAuth2\Storage\Memory(array('keys' => array( 'public_key' => $publicKey, 'private_key' => $privateKey, ))); $server = new OAuth2\Server($storage, array( 'use_jwt_access_tokens' => true, ));
這是使用JWT訪問令牌時的最小配置,而且將是ResourceController
惟一有效的。對於完整的服務器配置,您必須提供Client
存儲和一些受權類型。
如下是完整的服務器配置示例:
// token.php // error reporting (this is a demo, after all!) ini_set('display_errors',1);error_reporting(E_ALL); // Autoloading (composer is preferred, but for this example let's just do this) require_once('oauth2-server-php/src/OAuth2/Autoloader.php'); OAuth2\Autoloader::register(); // your public key strings can be passed in however you like // (there is a public/private key pair for testing already in the oauth library) $publicKey = file_get_contents('oauth2-server-php/test/config/keys/id_rsa.pub'); $privateKey = file_get_contents('oauth2-server-php/test/config/keys/id_rsa'); // create storage $storage = new OAuth2\Storage\Memory(array( 'keys' => array( 'public_key' => $publicKey, 'private_key' => $privateKey, ), // add a Client ID for testing 'client_credentials' => array( 'CLIENT_ID' => array('client_secret' => 'CLIENT_SECRET') ), )); $server = new OAuth2\Server($storage, array( 'use_jwt_access_tokens' => true, )); $server->addGrantType(new OAuth2\GrantType\ClientCredentials($storage)); // minimum config // send the response $server->handleTokenRequest(OAuth2\Request::createFromGlobals())->send();
如今您能夠調用您的服務器並接收JWT訪問令牌:
# start the PHP built-in web server $ php -S localhost:3000 & $ curl -i -v http://localhost:3000/token.php -u 'CLIENT_ID:CLIENT_SECRET' -d "grant_type=client_credentials"
服務器將返回一個包含JWT訪問令牌的響應:
{
"access_token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJpZCI6IjYzMjIwNzg0YzUzODA3ZjVmZTc2Yjg4ZjZkNjdlMmExZTIxODlhZTEiLCJjbGllbnRfaWQiOiJUZXN0IENsaWVudCBJRCIsInVzZXJfaWQiOm51bGwsImV4cGlyZXMiOjEzODAwNDQ1NDIsInRva2VuX3R5cGUiOiJiZWFyZXIiLCJzY29wZSI6bnVsbH0.PcC4k8Q_etpU-J4yGFEuBUdeyMJhtpZFkVQ__sXpe78eSi7xTniqOOtgfWa62Y4sj5Npta8xPuDglH8Fueh_APZX4wGCiRE1P4nT4APQCOTbgcuCNXwjmP8znk9F76ID2WxThaMbmpsTTEkuyyUYQKCCdxlIcSbVvcLZUGKZ6-g", "client_id":"CLIENT_ID", "user_id":null, "expires":1382630473, "scope":null }
資源服務器配置
若是您的資源服務器與您的受權服務器分開,則能夠在沒有受權服務器的私鑰的狀況下配置您的服務器:
/* for a Resource Server (minimum config) */ $publicKey = file_get_contents('/path/to/pubkey.pem'); // no private key necessary $keyStorage = new OAuth2\Storage\Memory(array('keys' => array( 'public_key' => $publicKey, ))); $server = new OAuth2\Server($keyStorage, array( 'use_jwt_access_tokens' => true, ));
這容許您的服務器驗證訪問令牌,而不向Authorization Server或任何其餘共享資源發出任何請求。
// verify the JWT Access Token in the request if (!$server->verifyResourceRequest(OAuth2\Request::createFromGlobals())) { exit("Failed"); } echo "Success!";
如今你能夠請求這個,並嘗試發送上面生成的令牌!
# start the PHP built-in web server $ php -S localhost:3000 & $ curl "http://localhost:3000/resource.php?access_token=eyJ0eXAi..." Success!
使用輔助存儲
該庫容許您將訪問令牌備份到輔助存儲。只是經過實施一個對象OAuth2\Storage\AccessTokenInterface
到JwtAccessToken
對象到具備存儲在一個附加的位置的訪問令牌:
$pdoStorage = new OAuth2\Storage\Pdo($pdo); // access token will also be saved to PDO $keyStorage = new OAuth2\Storage\Memory(array('keys' => array( 'public_key' => $publicKey, 'private_key' => $privateKey, )));
此示例從Memory
存儲中提取公鑰/私鑰,並Pdo
在簽名後將授予的訪問令牌保存到存儲。
特定於客戶端的加密密鑰
製做特定於客戶端的密鑰是一個好主意。這樣,若是密鑰對受到攻擊,只有一個客戶端受到影響。雙方Memory
並Pdo
支持這種類型的存儲。這裏是一個使用Memory
存儲的例子:
$keyStorage = new OAuth2\Storage\Memory(array('keys' => array( 'ClientID_One' => array( 'public_key' => file_get_contents('/path/to/client_1_rsa.pub'), 'private_key' => file_get_contents('/path/to/client_1_rsa'), ), 'ClientID_Two' => array( 'public_key' => file_get_contents('/path/to/client_2_rsa.pub'), 'private_key' => file_get_contents('/path/to/client_2_rsa'), ), // declare global keys as well 'public_key' => file_get_contents('/path/to/global_rsa.pub'), 'private_key' => file_get_contents('/path/to/global_rsa'), )));
對於Pdo
,運行如下查詢:
/* create the database table */ CREATE TABLE oauth_public_keys (client_id VARCHAR(80), public_key VARCHAR(8000), private_key VARCHAR(8000), encryption_algorithm VARCHAR(80) DEFAULT "RS256")
使用這樣的插入樣本數據:
/* insert global keys into the database */ INSERT INTO oauth_public_keys (client_id, public_key, private_key, encryption_algorithm) VALUES (NULL, "...", "...", "RS256"); /* add client-specific key pairs */ INSERT INTO oauth_public_keys (client_id, public_key, private_key, encryption_algorithm) VALUES ("ClientID_One", "...", "...", "RS256"); INSERT INTO oauth_public_keys (client_id, public_key, private_key, encryption_algorithm) VALUES ("ClientID_Two", "...", "...", "RS256");
並實例化PDO存儲對象:
$dsn = 'mysql:dbname=my_oauth2_db;host=localhost'; $username = 'root'; $password = ''; $pdoStorage = new OAuth2\Storage\Pdo(array('dsn' => $dsn, 'username' => $username, 'password' => $password));
配置不一樣的算法
JwtAccessTokens支持如下算法:
- 「HS256」 - 使用
hash_hmac
/ sha256 - 「HS384」 - 使用
hash_hmac
/ sha384 - 「HS512」 - 使用
hash_hmac
/ sha512 - 「RS256」 - 使用
openssl_sign
/ sha256 - 「RS384」 - 使用
openssl_sign
/ sha384 - 「RS512」 - 使用
openssl_sign
/ sha512
在你的OAuth2\Storage\PublicKeyInterface
實例中進行配置。當使用Memory
存儲時,這看起來像這樣:
$storage = new OAuth2\Storage\Memory(array('keys' => array( 'public_key' => $publicKey, 'private_key' => $privateKey, 'encryption_algorithm' => 'HS256', // "RS256" is the default )));
客戶端驗證
簽名能夠用任何編程語言進行驗證。使用標準的 Public Key
加密方法來驗證訪問令牌簽名。這是在PHP中的一個例子:
$token = json_decode($curlResponse); $jwt_access_token = $token['access_token']; $separator = '.'; if (2 !== substr_count($jwt_access_token, $separator)) { throw new Exception("Incorrect access token format"); } list($header, $payload, $signature) = explode($separator, $jwt_access_token); $decoded_signature = base64_decode(str_replace(array('-', '_'), array('+', '/'), $signature)); // The header and payload are signed together $payload_to_verify = utf8_decode($header . $separator . $payload); // however you want to load your public key $public_key = file_get_contents('/path/to/pubkey.pem'); // default is SHA256 $verified = openssl_verify($payload_to_verify, $decoded_signature, $public_key, OPENSSL_ALGO_SHA256); if ($verified !== 1) { throw new Exception("Cannot verify signature"); } // output the JWT Access Token payload var_dump(base64_decode($payload));