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