安全性 - JSON Web 令牌 (JWT)¶
概览¶
注意
当前仅支持对称算法
Phalcon\Encryption\Security\JWT
是一个命名空间,其中包含的组件允许你按照RFC 7915中的描述来发放、解析和验证 JSON Web 令牌。
- 构造器 (Phalcon\Encryption\Security\JWT\Builder)
- 解析器 (Phalcon\Encryption\Security\JWT\Token\Parser)
- 验证器 (Phalcon\Encryption\Security\JWT\Validator)
注意
在以下示例中,为了便于阅读,将输出分成了不同行
使用该组件的一个示例如下:
<?php
use Phalcon\Encryption\Security\JWT\Builder;
use Phalcon\Encryption\Security\JWT\Signer\Hmac;
use Phalcon\Encryption\Security\JWT\Token\Parser;
use Phalcon\Encryption\Security\JWT\Validator;
// Defaults to 'sha512'
$signer = new Hmac();
// Builder object
$builder = new Builder($signer);
$now = new DateTimeImmutable();
$issued = $now->getTimestamp();
$notBefore = $now->modify('-1 minute')->getTimestamp();
$expires = $now->modify('+1 day')->getTimestamp();
$passphrase = 'QcMpZ&b&mo3TPsPk668J6QH8JA$&U&m2';
// Setup
$builder
->setAudience('https://target.phalcon.io') // aud
->setContentType('application/json') // cty - header
->setExpirationTime($expires) // exp
->setId('abcd123456789') // JTI id
->setIssuedAt($issued) // iat
->setIssuer('https://phalcon.io') // iss
->setNotBefore($notBefore) // nbf
->setSubject('my subject for this claim') // sub
->setPassphrase($passphrase) // password
;
// Phalcon\Encryption\Security\JWT\Token\Token
$tokenObject = $builder->getToken();
echo $tokenObject->getToken();
// eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiIsImN0eSI6ImFwcGxpY2F0aW9uXC9qc29uIn0.
// eyJhdWQiOlsiaHR0cHM6XC9cL3RhcmdldC5waGFsY29uLmlvIl0sImV4cCI6MTYxNDE4NTkxN
// ywianRpIjoiYWJjZDEyMzQ1Njc4OSIsImlhdCI6MTYxNDA5OTUxNywiaXNzIjoiaHR0cHM6XC
// 9cL3BoYWxjb24uaW8iLCJuYmYiOjE2MTQwOTk0NTcsInN1YiI6Im15IHN1YmplY3QgZm9yIHR
// oaXMgY2xhaW0ifQ.
// LdYevRZaQDZ2lul4CCQ5DymeP2ubcapTtgeezOZGIq7Meu7rFF1pv32b-AMWOxCS63CQz_jpm
// BPlPyOeEAkMbg
// $tokenReceived is what we received
$tokenReceived = getMyTokenFromTheApplication();
$audience = 'https://target.phalcon.io';
$now = new DateTimeImmutable();
$issued = $now->getTimestamp();
$notBefore = $now->modify('-1 minute')->getTimestamp();
$expires = $now->getTimestamp();
$id = 'abcd123456789';
$issuer = 'https://phalcon.io';
// Defaults to 'sha512'
$signer = new Hmac();
$passphrase = 'QcMpZ&b&mo3TPsPk668J6QH8JA$&U&m2';
// Parse the token
$parser = new Parser();
// Phalcon\Encryption\Security\JWT\Token\Token
$tokenObject = $parser->parse($tokenReceived);
// Phalcon\Encryption\Security\JWT\Validator
$validator = new Validator($tokenObject, 100); // Allow for a time shift of 100
# Run the validators
$validator
->validateAudience($audience)
->validateExpiration($expires)
->validateId($id)
->validateIssuedAt($issued)
->validateIssuer($issuer)
->validateNotBefore($notBefore)
->validateSignature($signer, $passphrase)
;
# Errors printed out (if any)
var_dump($validator->getErrors())
上面的示例总体展示了如何使用该组件生成、解析和验证 JSON Web 令牌。
对象¶
在Phalcon\Encryption\Security\JWT\Token
命名空间中有一些实用工具组件,
枚举¶
Phalcon\Encryption\Security\JWT\Token\Enum是一个包含多个常量的类。这些常量是定义在RFC 7915中的字符串。你可以选择使用它们,也可以使用对应的字符串代替。
<?php
class Enum
{
/**
* Headers
*/
const TYPE = "typ";
const ALGO = "alg";
const CONTENT_TYPE = "cty";
/**
* Claims
*/
const AUDIENCE = "aud";
const EXPIRATION_TIME = "exp";
const ID = "jti";
const ISSUED_AT = "iat";
const ISSUER = "iss";
const NOT_BEFORE = "nbf";
const SUBJECT = "sub";
}
条目¶
Phalcon\Encryption\Security\JWT\Token\Item内部用于存储有效载荷及其编码状态。此类有效载荷可以是声明数据或头部数据。通过使用此组件,我们可以轻松提取每个令牌所需的必要信息。
签名¶
Phalcon\Encryption\Security\JWT\Token\Signature与Phalcon\Encryption\Security\JWT\Token\Item类似,但它仅保存签名哈希及其编码值。
令牌¶
Phalcon\Encryption\Security\JWT\Token\Token是负责存储并计算 JWT 令牌的组件。其构造函数接收头部、声明(作为Phalcon\Encryption\Security\JWT\Token\Item对象)以及签名对象,并提供如下方法:
返回声明集合 返回头部集合 返回有效载荷。对于一个令牌来说,abcd.efgh.ijkl
它将返回abcd.efgh
返回签名 将令牌作为字符串返回。对于一个令牌来说,abcd.efgh.ijkl
它将返回abcd.efgh.ijkl
. 对令牌数据运行所有验证器。返回验证器中的错误数组 验证 token 的签名 签发者¶
要创建一个 JWT 令牌,我们需要提供一个签名算法。默认情况下,构造器使用 "none" (Phalcon\Encryption\Security\JWT\Signer\None)。但你可以使用 HMAC 签发者 (Phalcon\Encryption\Security\JWT\Signer\Hmac)。此外,为进一步自定义,你可以使用提供的Phalcon\Encryption\Security\JWT\Signer\SignerInterface接口。
None
此签发者主要用于开发目的。你应该始终对你 JWT 令牌进行签名。
HMAC
HMAC 签发者支持sha512
, sha384
和sha256
算法。如果不指定,则会自动选择sha512
。如果你指定了不同的算法,则会抛出Phalcon\Encryption\Security\JWT\Exceptions\UnsupportedAlgorithmException异常。算法在构造函数中设置。
<?php
use Phalcon\Encryption\Security\JWT\Signer\Hmac;
$signer = new Hmac();
$signer = new Hmac('sha512');
$signer = new Hmac('sha384');
$signer = new Hmac('sha256');
$signer = new Hmac('sha111'); // exception
该组件内部使用 [hmac_equals][hmac_equals] 和 [hash_hmac][hash_hmac] PHP 方法来验证和签署有效载荷。它提供了以下方法:
返回标识该算法的字符串。对于 HMAC 算法,它将返回:
算法 | getAlgHeader |
---|---|
sha512 | HS512 |
sha384 | HS384 |
sha256 | HS256 |
使用密钥短语返回有效载荷的哈希值
验证源字符串的哈希是否与使用密钥短语对有效载荷进行哈希后的结果一致。
发放令牌¶
提供了一个 Builder 组件 (Phalcon\Encryption\Security\JWT\Builder),利用链式调用方法,并准备好用于创建 JWT 令牌。你只需实例化 Builder 对象,配置好你的令牌,然后调用getToken()
方法即可。这将返回一个Phalcon\Encryption\Security\Token\Token对象,其中包含你的令牌所需的所有必要信息。在实例化 builder 组件时,你必须提供签发者类。在下面的示例中我们使用了Phalcon\Encryption\Security\JWT\Signer\Hmac签发者。
该组件中的所有设置方法都支持链式调用。
<?php
use Phalcon\Encryption\Security\JWT\Builder;
use Phalcon\Encryption\Security\JWT\Signer\Hmac;
// Defaults to 'sha512'
$signer = new Hmac();
// Builder object
$builder = new Builder($signer);
方法¶
构造函数 初始化对象 - 当你想重复使用同一个 builder 时很有用 向声明集合中添加一个自定义声明 返回aud
内容 返回声明作为一个数组 返回内容类型 (cty
- 头部) 返回exp
内容 返回头部作为一个数组 返回jti
内容 (该 JWT 的 ID) 返回iat
内容 返回iss
内容 返回nbf
内容 返回sub
内容 返回令牌 返回提供的密钥短语 设置受众 (aud
)。如果传入的参数不是一个数组或者字符串,将会抛出Phalcon\Encryption\Security\JWT\Exceptions\ValidatorException异常。 设置内容类型 (cty
- 头部) 设置受众 (exp
)。如果当前时间晚于$timestamp
,则会抛出Phalcon\Encryption\Security\JWT\Exceptions\ValidatorException异常。 设置 id (jti
)。 设置发布时间 (iat
)。 设置签发者 (iss
)。 设置生效时间 (nbf
)。如果当前时间晚于$timestamp
,则会抛出Phalcon\Encryption\Security\JWT\Exceptions\ValidatorException异常。 设置主题 (sub
)。 设置密钥短语。如果$passphrase
过于简单,则会抛出Phalcon\Encryption\Security\JWT\Exceptions\ValidatorException异常。 在内部集合中设置一个声明值。 示例¶
<?php
use Phalcon\Encryption\Security\JWT\Builder;
use Phalcon\Encryption\Security\JWT\Signer\Hmac;
use Phalcon\Encryption\Security\JWT\Token\Parser;
use Phalcon\Encryption\Security\JWT\Validator;
// 'sha512'
$signer = new Hmac();
$builder = new Builder($signer);
$now = new DateTimeImmutable();
$issued = $now->getTimestamp();
$notBefore = $now->modify('-1 minute')->getTimestamp();
$expires = $now->modify('+1 day')->getTimestamp();
$passphrase = 'QcMpZ&b&mo3TPsPk668J6QH8JA$&U&m2';
$builder
->setAudience('https://target.phalcon.io') // aud
->setContentType('application/json') // cty - header
->setExpirationTime($expires) // exp
->setId('abcd123456789') // JTI id
->setIssuedAt($issued) // iat
->setIssuer('https://phalcon.io') // iss
->setNotBefore($notBefore) // nbf
->setSubject('my subject for this claim') // sub
->setPassphrase($passphrase) // password
;
// Phalcon\Encryption\Security\JWT\Token\Token
$tokenObject = $builder->getToken();
echo $tokenObject->getToken();
// eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiIsImN0eSI6ImFwcGxpY2F0aW9uXC9qc29uIn0.
// eyJhdWQiOlsiaHR0cHM6XC9cL3RhcmdldC5waGFsY29uLmlvIl0sImV4cCI6MTYxNDE4NTkxN
// ywianRpIjoiYWJjZDEyMzQ1Njc4OSIsImlhdCI6MTYxNDA5OTUxNywiaXNzIjoiaHR0cHM6XC
// 9cL3BoYWxjb24uaW8iLCJuYmYiOjE2MTQwOTk0NTcsInN1YiI6Im15IHN1YmplY3QgZm9yIHR
// oaXMgY2xhaW0ifQ.
// LdYevRZaQDZ2lul4CCQ5DymeP2ubcapTtgeezOZGIq7Meu7rFF1pv32b-AMWOxCS63CQz_jpm
// BPlPyOeEAkMbg
验证令牌¶
要验证一个令牌,你需要创建一个新的Phalcon\Encryption\Security\JWT\Validator对象。该对象可以通过传递一个Phalcon\Encryption\Security\JWT\Token\Token对象和一个时间偏移量,用于处理发送方与接收方计算机之间的时间/时钟偏差。
要解析收到的 JWT 并将其转换为Phalcon\Encryption\Security\JWT\Token\Token对象,你需要使用一个Phalcon\Encryption\Security\JWT\Token\Parser对象并进行解析。
验证器¶
$parser = new Parser();
$tokenObject = $parser->parse($tokenReceived);
$validator = new Validator($tokenObject, 100); // allow for a time shift of 100
validate*
methods with the necessary parameters (taken from the Phalcon\Encryption\Security\Token\Token)。内部的errors
数组在Phalcon\Encryption\Security\JWT\Validator中会被相应地填充,并通过getErrors()
方法设置容器。 方法¶
构造函数 返回声明(claim)的值 ——null
如果该声明不存在,则返回 设置声明及其值。 设置 token 对象。 验证受众(audience)。如果 token 中不包含该aud
声明,Phalcon\Encryption\Security\JWT\Exceptions\ValidatorException异常。 验证过期时间。如果 token 中存储的exp
时间大于当前时间,则抛出Phalcon\Encryption\Security\JWT\Exceptions\ValidatorException异常。 验证 ID。如果与 token 中存储的jti
不相同,则抛出Phalcon\Encryption\Security\JWT\Exceptions\ValidatorException异常。 验证issued at
时间。如果 token 中存储的iat
时间大于当前时间,则抛出Phalcon\Encryption\Security\JWT\Exceptions\ValidatorException异常。 验证签发者(issuer)。如果与 token 中存储的iss
不相同,则抛出Phalcon\Encryption\Security\JWT\Exceptions\ValidatorException异常。 验证生效时间(not before time)。如果 token 中存储的nbf
时间大于当前时间,则抛出Phalcon\Encryption\Security\JWT\Exceptions\ValidatorException异常。 验证 token 的签名。如果签名无效,则抛出Phalcon\Encryption\Security\JWT\Exceptions\ValidatorException异常。 示例¶
<?php
use Phalcon\Encryption\Security\JWT\Signer\Hmac;
use Phalcon\Encryption\Security\JWT\Token\Parser;
use Phalcon\Encryption\Security\JWT\Validator;
// eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiIsImN0eSI6ImFwcGxpY2F0aW9uXC9qc29uIn0.
// eyJhdWQiOlsiaHR0cHM6XC9cL3RhcmdldC5waGFsY29uLmlvIl0sImV4cCI6MTYxNDE4NTkxN
// ywianRpIjoiYWJjZDEyMzQ1Njc4OSIsImlhdCI6MTYxNDA5OTUxNywiaXNzIjoiaHR0cHM6XC
// 9cL3BoYWxjb24uaW8iLCJuYmYiOjE2MTQwOTk0NTcsInN1YiI6Im15IHN1YmplY3QgZm9yIHR
// oaXMgY2xhaW0ifQ.
// LdYevRZaQDZ2lul4CCQ5DymeP2ubcapTtgeezOZGIq7Meu7rFF1pv32b-AMWOxCS63CQz_jpm
// BPlPyOeEAkMbg
$tokenReceived = getMyTokenFromTheApplication();
$audience = 'https://target.phalcon.io';
$now = new DateTimeImmutable();
$issued = $now->getTimestamp();
$notBefore = $now->modify('-1 minute')->getTimestamp();
$expires = $now->getTimestamp();
$id = 'abcd123456789';
$issuer = 'https://phalcon.io';
// 'sha512'
$signer = new Hmac();
$passphrase = 'QcMpZ&b&mo3TPsPk668J6QH8JA$&U&m2';
$parser = new Parser();
// Phalcon\Encryption\Security\JWT\Token\Token
$tokenObject = $parser->parse($tokenReceived);
// Phalcon\Encryption\Security\JWT\Validator
$validator = new Validator($tokenObject, 100); // allow for a time shift of 100
$validator
->validateAudience($audience)
->validateExpiration($expires)
->validateId($id)
->validateIssuedAt($issued)
->validateIssuer($issuer)
->validateNotBefore($notBefore)
->validateSignature($signer, $passphrase)
;
var_dump($validator->getErrors());
令牌¶
另外,你也可以使用verify()
和validate()
your token using the relevant methods in the Phalcon\Encryption\Security\Token\Token对象中的相关方法手动
方法¶
验证 token 的声明。执行的验证器包括:validateAudience()
validateExpiration()
validateId()
validateIssuedAt()
validateIssuer()
validateNotBefore()
你可以扩展Phalcon\Encryption\Security\JWT\Validator和Phalcon\Encryption\Security\Token\Token对象以添加更多的验证器并在其中执行它们(如下所示)。
验证 token 的签名示例¶
<?php
use Phalcon\Encryption\Security\JWT\Enum;
use Phalcon\Encryption\Security\JWT\Signer\Hmac;
use Phalcon\Encryption\Security\JWT\Token\Parser;
use Phalcon\Encryption\Security\JWT\Validator;
// eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiIsImN0eSI6ImFwcGxpY2F0aW9uXC9qc29uIn0.
// eyJhdWQiOlsiaHR0cHM6XC9cL3RhcmdldC5waGFsY29uLmlvIl0sImV4cCI6MTYxNDE4NTkxN
// ywianRpIjoiYWJjZDEyMzQ1Njc4OSIsImlhdCI6MTYxNDA5OTUxNywiaXNzIjoiaHR0cHM6XC
// 9cL3BoYWxjb24uaW8iLCJuYmYiOjE2MTQwOTk0NTcsInN1YiI6Im15IHN1YmplY3QgZm9yIHR
// oaXMgY2xhaW0ifQ.
// LdYevRZaQDZ2lul4CCQ5DymeP2ubcapTtgeezOZGIq7Meu7rFF1pv32b-AMWOxCS63CQz_jpm
// BPlPyOeEAkMbg
$tokenReceived = getMyTokenFromTheApplication();
$subject = 'Mary had a little lamb';
$audience = 'https://target.phalcon.io';
$now = new DateTimeImmutable();
$issued = $now->getTimestamp();
$notBefore = $now->modify('-1 minute')->getTimestamp();
$expires = $now->getTimestamp();
$id = 'abcd123456789';
$issuer = 'https://phalcon.io';
// 'sha512'
$signer = new Hmac();
$passphrase = 'QcMpZ&b&mo3TPsPk668J6QH8JA$&U&m2';
$parser = new Parser();
// Phalcon\Encryption\Security\JWT\Token\Token
$tokenObject = $parser->parse($tokenReceived);
// Phalcon\Encryption\Security\JWT\Validator
$validator = new Validator($tokenObject, 100); // allow for a time shift of 100
$validator
->set(Enum::AUDIENCE, $audience)
->set(Enum::EXPIRATION_TIME, $expiry)
->set(Enum::ISSUER, $issuer)
->set(Enum::ISSUED_AT, $issued)
->set(Enum::ID, $id)
->set(Enum::NOT_BEFORE, $notBefore)
->set(Enum::SUBJECT, $subject)
;
$tokenObject->verify($signer, $passphrase);
$errors = $tokenObject->validate($validator);
var_dump($errors);
异常¶
Security 组件中抛出的任何异常都属于命名空间Phalcon\Encryption\Security\JWT\*
。你可以利用这个异常有选择性地捕获仅来自该组件的异常。此处会引发两个异常:第一个异常是在实例化Phalcon\Encryption\Security\JWT\Signer\Hmac组件时传入了错误的算法字符串。此异常是Phalcon\Encryption\Security\JWT\Exceptions\UnsupportedAlgorithmException.
第二个异常在验证 JWT 时抛出。此异常是Phalcon\Encryption\Security\JWT\Exceptions\ValidatorException.
<?php
use Phalcon\Mvc\Controller;
use Phalcon\Encryption\Security\JWT\Builder;
use Phalcon\Encryption\Security\JWT\Exceptions\ValidatorException;
use Phalcon\Encryption\Security\JWT\Signer\Hmac;
use Phalcon\Encryption\Security\JWT\Validator;
class IndexController extends Controller
{
public function index()
{
try {
$signer = new Hmac();
$builder = new Builder($signer);
$expiry = strtotime('+1 day');
$issued = strtotime('now') + 100;
$notBefore = strtotime('-1 day');
$passphrase = '&vsJBETaizP3A3VX&TPMJUqi48fJEgN7';
return $builder
->setAudience('my-audience')
->setExpirationTime($expiry)
->setIssuer('Phalcon JWT')
->setIssuedAt($issued)
->setId('PH-JWT')
->setNotBefore($notBefore)
->setSubject('Mary had a little lamb')
->setPassphrase($passphrase)
->getToken()
;
$validator = new Validator($token);
$validator->validateAudience("unknown");
} catch (Exception $ex) {
echo $ex->getMessage(); // Validation: audience not allowed
}
}
}