智威汤逊访问令牌
概观
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));