miladrahimi/php-jwt

JWT (JSON Web Token) 生成器、解析器、验证器和验证器的 PHP 实现

v3.2.2 2024-06-01 10:40 UTC

This package is auto-updated.

Last update: 2024-09-19 20:46:03 UTC


README

Latest Stable Version Total Downloads Build codecov Scrutinizer Code Quality License

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 包支持 HS256HS384HS512 HMAC 算法。上面的示例展示了如何使用 HMAC 算法对 JWT 进行签名和验证。

RSA 算法

RSA 算法使用密钥对:用于签名 JWT 的私钥和相应的公钥用于验证。当认证服务器不能完全信任资源所有者时,此方法很有用。PHP-JWT 包支持 RS256RS384RS512 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 包支持 ES256ES256KRS384 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。我们强烈建议您为您的验证扩展它。请注意,DefaultValidatorBaseValidator的子类。虽然您可以使用BaseValidator进行验证,但选择这种方式意味着您将失去内置规则,需要您手动添加所有规则。

规则

验证器依赖于规则来验证声明,每个规则指定了声明的可接受值。您可以在MiladRahimi\Jwt\Validator\Rules命名空间内访问内置规则。

每个规则的描述可以在其各自的类文档块中找到。

自定义规则

如果提供的内置规则不能满足您的需求,您可以创建自定义规则。为此,实现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]

错误处理

以下是该软件包可能会抛出的异常

所有这些异常都是JwtException异常的子类。通过捕获JwtException,您可以集体处理所有这些情况,而不是单独捕获每个异常。

许可

PHP-JWT最初由Milad Rahimi创建,并发布在MIT许可证下。