miladrahimi / php-jwt
JWT (JSON Web Token) 生成器、解析器、验证器和验证器的 PHP 实现
Requires
- php: >=7.4
- ext-json: *
- ext-openssl: *
Requires (Dev)
- phpunit/phpunit: ^7|^8|^9
Suggests
- ext-sodium: Sodium extension is required for EdDSA algortihms
README
PHP-JWT
PHP-JWT 是一个用于编码(生成)、解码(解析)、验证和验证 JSON Web Tokens(JWTs)的 PHP 包。其设计强调流畅、用户友好且面向对象的接口,注重性能。
支持算法
- HMAC:
HS256
,HS384
, 和HS512
- RSA:
RS256
,RS384
, 和RS512
- ECDSA:
ES256
,ES256K
, 和RS384
- EdDSA:
EdDSA
支持特性
- 内置和自定义验证
- 多个密钥和
kid
头部处理器
由 JWT.io 验证。
文档
什么是 JWT?
如果您不熟悉 JWT,可以参考 维基百科页面 或访问 JWT.io 获取更多信息。
安装
使用以下命令将包包含在您的 Composer 依赖项中
composer require miladrahimi/php-jwt "3.*"
快速入门
以下是一个示例,展示了如何使用 HS256
算法生成 JWT 并解析它
use MiladRahimi\Jwt\Generator; use MiladRahimi\Jwt\Parser; use MiladRahimi\Jwt\Cryptography\Keys\HmacKey; use MiladRahimi\Jwt\Cryptography\Algorithms\Hmac\HS256; // Use HS256 to generate and parse JWTs $key = new HmacKey('12345678901234567890123456789012'); $signer = new HS256($key); // Generate a JWT $generator = new Generator($signer); $jwt = $generator->generate(['id' => 13, 'is-admin' => true]); print_r($jwt); // "abc.123.xyz" // Parse the token $parser = new Parser($signer); $claims = $parser->parse($jwt); print_r($claims); // ['id' => 13, 'is-admin' => true]
HMAC 算法
HMAC 算法依赖于对称密钥,允许单个密钥编码(签名)和解码(验证)JWT。PHP-JWT 包支持 HS256
、HS384
和 HS512
HMAC 算法。上面的示例展示了如何使用 HMAC 算法对 JWT 进行签名和验证。
RSA 算法
RSA 算法使用密钥对:用于签名 JWT 的私钥和相应的公钥用于验证。当认证服务器不能完全信任资源所有者时,此方法很有用。PHP-JWT 包支持 RS256
、RS384
和 RS512
RSA 算法。下面的示例演示了此过程。
use MiladRahimi\Jwt\Cryptography\Algorithms\Rsa\RS256Signer; use MiladRahimi\Jwt\Cryptography\Algorithms\Rsa\RS256Verifier; use MiladRahimi\Jwt\Cryptography\Keys\RsaPrivateKey; use MiladRahimi\Jwt\Cryptography\Keys\RsaPublicKey; use MiladRahimi\Jwt\Generator; use MiladRahimi\Jwt\Parser; // Generate a token $privateKey = new RsaPrivateKey('/path/to/private.pem'); $signer = new RS256Signer($privateKey); $generator = new Generator($signer); $jwt = $generator->generate(['id' => 13, 'is-admin' => true]); print_r($jwt); // "abc.123.xyz" // Parse the token $publicKey = new RsaPublicKey('/path/to/public.pem'); $verifier = new RS256Verifier($publicKey); $parser = new Parser($verifier); $claims = $parser->parse($jwt); print_r($claims); // ['id' => 13, 'is-admin' => true]
您可以通过 此说明 学习如何使用 OpenSSL 生成 RSA 密钥对。
ECDSA 算法
ECDSA 算法与 RSA 类似,也是非对称算法,提供了比 RSA 更强的安全措施。PHP-JWT 包支持 ES256
、ES256K
和 RS384
ECDSA 算法。下面的示例演示了此过程。
use MiladRahimi\Jwt\Cryptography\Algorithms\Ecdsa\ES384Signer; use MiladRahimi\Jwt\Cryptography\Algorithms\Ecdsa\ES384Verifier; use MiladRahimi\Jwt\Cryptography\Keys\EcdsaPrivateKey; use MiladRahimi\Jwt\Cryptography\Keys\EcdsaPublicKey; use MiladRahimi\Jwt\Generator; use MiladRahimi\Jwt\Parser; // Generate a token $privateKey = new EcdsaPrivateKey('/path/to/private.pem'); $signer = new ES384Signer($privateKey); $generator = new Generator($signer); $jwt = $generator->generate(['id' => 13, 'is-admin' => true]); print_r($jwt); // "abc.123.xyz" // Parse the token $publicKey = new EcdsaPublicKey('/path/to/public.pem'); $verifier = new ES384Verifier($publicKey); $parser = new Parser($verifier); $claims = $parser->parse($jwt); print_r($claims); // ['id' => 13, 'is-admin' => true]
EdDSA 算法
EdDSA 与 RSA 和 ECDSA 类似,是一种非对称加密算法,并被广泛推荐。为了使用它,请确保您的环境中已安装 sodium
PHP 扩展。以下示例演示了如何使用它。
use MiladRahimi\Jwt\Cryptography\Algorithms\Eddsa\EdDsaSigner; use MiladRahimi\Jwt\Cryptography\Algorithms\Eddsa\EdDsaVerifier; use MiladRahimi\Jwt\Generator; use MiladRahimi\Jwt\Parser; // Generate a token $privateKey = new EdDsaPrivateKey(base64_decode(file_get_contents('/path/to/ed25519.sec'))); $signer = new EdDsaSigner($privateKey); $generator = new Generator($signer); $jwt = $generator->generate(['id' => 13, 'is-admin' => true]); print_r($jwt); // "abc.123.xyz" // Parse the token $publicKey = new EdDsaPublicKey(base64_decode(file_get_contents('/path/to/ed25519.pub'))); $verifier = new EdDsaVerifier($publicKey); $parser = new Parser($verifier); $claims = $parser->parse($jwt); print_r($claims); // ['id' => 13, 'is-admin' => true]
请注意,EdDSA 密钥必须是字符串格式。如果它们已经是 base64 编码的,则在使用之前必须解码它们。
验证
默认情况下,如果存在,该包会验证某些公共声明(使用 DefaultValidator
),并解析声明。如果您有自定义声明,还可以包括它们的验证规则。查看此示例
use MiladRahimi\Jwt\Parser; use MiladRahimi\Jwt\Cryptography\Algorithms\Hmac\HS256; use MiladRahimi\Jwt\Exceptions\ValidationException; use MiladRahimi\Jwt\Validator\Rules\EqualsTo; $jwt = '...'; // Get the JWT from the user $signer = new HS256(new HmacKey('12345678901234567890123456789012')); // Extend the DefaultValidator $validator = new DefaultValidator(); // The 'is-admin' claim is required, without it or a mismatched rule, validation fails. $validator->addRequiredRule('is-admin', new EqualsTo(true)); // The 'exp' claim is optional, and the rule will be applicable if it is present. $validator->addOptionalRule('exp', new NewerThan(time()), false); // Parse the token $parser = new Parser($signer, $validator); try { $claims = $parser->parse($jwt); print_r($claims); // ['id' => 13, 'is-admin' => true] } catch (ValidationException $e) { // Handle error. }
在上述示例中,我们扩展了带有预定义规则的DefaultValidator
。我们强烈建议您为您的验证扩展它。请注意,DefaultValidator
是BaseValidator
的子类。虽然您可以使用BaseValidator
进行验证,但选择这种方式意味着您将失去内置规则,需要您手动添加所有规则。
规则
验证器依赖于规则来验证声明,每个规则指定了声明的可接受值。您可以在MiladRahimi\Jwt\Validator\Rules
命名空间内访问内置规则。
- ConsistsOf
- EqualsTo
- GreaterThan
- GreaterThanOrEqualTo
- IdenticalTo
- LessThan
- LessThanOrEqualTo
- NewerThan
- NewerThanOrSame
- NotEmpty
- NotNull
- OlderThan
- OlderThanOrSame
每个规则的描述可以在其各自的类文档块中找到。
自定义规则
如果提供的内置规则不能满足您的需求,您可以创建自定义规则。为此,实现Rule
接口。例如,下面是一个Even
规则,用于验证给定的声明是否代表偶数
use MiladRahimi\Jwt\Exceptions\ValidationException; use MiladRahimi\Jwt\Validator\Rule; class Even implements Rule { public function validate(string $name, $value) { if ($value % 2 != 0) { throw new ValidationException("The `$name` must be an even number."); } } }
多个密钥
JWT头部的kid
参数在有效管理多个密钥方面发挥着至关重要的作用。通过利用“kid”头部,您可以给每个用于签名JWT的密钥分配一个唯一的密钥标识符(kid)。这使得通过将JWT与其相应的密钥标识符(kid)关联起来,无缝验证JWT成为可能。查看此示例
use MiladRahimi\Jwt\Cryptography\Algorithms\Ecdsa\ES384Signer; use MiladRahimi\Jwt\Cryptography\Algorithms\Ecdsa\ES384Verifier; use MiladRahimi\Jwt\Cryptography\Algorithms\Rsa\RS256Signer; use MiladRahimi\Jwt\Cryptography\Algorithms\Rsa\RS256Verifier; use MiladRahimi\Jwt\Cryptography\Keys\EcdsaPrivateKey; use MiladRahimi\Jwt\Cryptography\Keys\EcdsaPublicKey; use MiladRahimi\Jwt\Cryptography\Keys\RsaPrivateKey; use MiladRahimi\Jwt\Cryptography\Keys\RsaPublicKey; use MiladRahimi\Jwt\Generator; use MiladRahimi\Jwt\Parser; $privateKey1 = new RsaPrivateKey('/path/to/rsa-private.pem', '', 'key-1'); $publicKey1 = new RsaPublicKey('/path/to/rsa-public.pem', 'key-1'); $privateKey2 = new EcdsaPrivateKey('/path/to/ecdsa384-private.pem', '', 'key-2'); $publicKey2 = new EcdsaPublicKey('/path/to/ecdsa384-public.pem', 'key-2'); // Generate tokens $signer1 = new RS256Signer($privateKey1); $generator1 = new Generator($signer1); $jwt1 = $generator1->generate(['id' => 13, 'is-admin' => true]); // $jwt1 header: {"alg": "RS256", "typ": "JWT", "kid": "key-1"} $signer2 = new ES384Signer($privateKey2); $generator2 = new Generator($signer2); $jwt2 = $generator2->generate(['id' => 13, 'is-admin' => true]); // $jwt2 header: {"alg": "ES384", "typ": "JWT", "kid": "key-2"} // Parse tokens $verifierFactory = new VerifierFactory([ new RS256Verifier($publicKey1), new ES384Verifier($publicKey2), ]); $verifier1 = $verifierFactory->getVerifier($jwt1); // instance of RS256Verifier $parser1 = new Parser($verifier1); $claims1 = $parser1->parse($jwt1); print_r($claims1); // ['id' => 13, 'is-admin' => true] $verifier2 = $verifierFactory->getVerifier($jwt2); // instance of ES384Verifier $parser2 = new Parser($verifier2); $claims2 = $parser2->parse($jwt2); print_r($claims2); // ['id' => 13, 'is-admin' => true]
错误处理
以下是该软件包可能会抛出的异常
- 编码
- InvalidKeyException 当提供的密钥无效时。
- JsonEncodingException 当无法将提供的声明转换为JSON时。
- SigningException 当无法使用提供的签名者或密钥签名令牌时。
- 解码
- InvalidTokenException 当JWT格式无效时(例如,它没有负载)。
- InvalidSignatureException 当JWT签名无效时。
- JsonDecodingException 当从JWT提取的JSON无效时。
- ValidationException 当至少有一个验证规则失败时。
- 查找验证器
- NoKidException 当令牌头部中没有
kid
时。 - VerifierNotFoundException 当没有密钥/验证器与令牌头部的
kid
匹配时。
- NoKidException 当令牌头部中没有
所有这些异常都是JwtException异常的子类。通过捕获JwtException
,您可以集体处理所有这些情况,而不是单独捕获每个异常。
许可
PHP-JWT最初由Milad Rahimi创建,并发布在MIT许可证下。