lake/larke-jwt

一个用于处理JSON Web Token和JSON Web Signature的库。

1.3.0 2024-03-14 04:32 UTC

This package is auto-updated.

Last update: 2024-09-14 05:34:10 UTC


README

一个用于处理JSON Web Token和JSON Web Signature的简单库(需要PHP 5.6+)。实现基于RFC 7519

代码是从lcobucci/jwt分叉而来

安装

您可以使用Composer进行安装。

composer require lake/larke-jwt

依赖

  • PHP >= 8.1.0
  • OpenSSL扩展
  • sodium扩展

基本用法

创建

只需使用构建器创建新的JWT/JWS令牌

use DateTimeImmutable;
use Larke\JWT\Builder;
use Larke\JWT\Signer\None;
use Larke\JWT\Signer\Key\InMemory;

$now    = new DateTimeImmutable();
$signer = new None();
$key    = InMemory::plainText('testing')

$token = (new Builder())
    ->issuedBy('http://example.com') // Configures the issuer (iss claim)
    ->permittedFor('http://example.org') // Configures the audience (aud claim)
    ->identifiedBy('4f1g23a12aa', true) // Configures the id (jti claim), replicating as a header item
    ->issuedAt($now) // Configures the time that the token was issue (iat claim)
    ->canOnlyBeUsedAfter($now->modify('+1 minute')) // Configures the time that the token can be used (nbf claim)
    ->expiresAt($now->modify('+1 hour')) // Configures the expiration time of the token (exp claim)
    ->withClaim('uid', 1) // Configures a new claim, called "uid"
    ->getToken($signer, $key); // Retrieves the generated token

$token->headers()->all(); // Retrieves the token headers
$token->claims()->all(); // Retrieves the token claims

echo $token->headers()->get('jti'); // will print "4f1g23a12aa"
echo $token->claims()->get('iss'); // will print "http://example.com"
echo $token->claims()->get('uid'); // will print "1"
echo $token->toString(); // The string representation of the object is a JWT string (pretty easy, right?)

从字符串解析

使用解析器从JWT字符串创建新的令牌(使用之前的令牌作为示例)

use Larke\JWT\Parser;

$token = (new Parser())->parse((string) $token); // Parses from a string
$token->headers()->all(); // Retrieves the token headers
$token->claims()->all(); // Retrieves the token claims

echo $token->headers()->get('jti'); // will print "4f1g23a12aa"
echo $token->claims()->get('iss'); // will print "http://example.com"
echo $token->claims()->get('uid'); // will print "1"

验证

我们可以轻松地验证令牌是否有效(使用之前的令牌和时间作为示例)

use DateTimeImmutable;
use Larke\JWT\Validator;
use Larke\JWT\ValidationData;

$now = new DateTimeImmutable();

$data = new ValidationData(); // It will use the current time to validate (iat, nbf and exp)
$data->issuedBy('http://example.com');
$data->permittedFor('http://example.org');
$data->identifiedBy('4f1g23a12aa');

$validation = new Validator();

var_dump($validation->validate($token, $data)); // false, because token cannot be used before now() + 60

$data->currentTime($now->modify('+61 seconds')); // changing the validation time to future

var_dump($validation->validate($token, $data)); // true, because current time is between "nbf" and "exp" claims

$data->currentTime($now->modify('+4000 seconds')); // changing the validation time to future

var_dump($validation->validate($token, $data)); // false, because token is expired since current time is greater than exp

// We can also use the $leeway parameter to deal with clock skew (see notes below)
// If token's claimed now is invalid but the difference between that and the validation time is less than $leeway, 
// then token is still considered valid
$dataWithLeeway = new ValidationData($now, 20); 
$dataWithLeeway->issuedBy('http://example.com');
$dataWithLeeway->permittedFor('http://example.org');
$dataWithLeeway->identifiedBy('4f1g23a12aa');

var_dump($validation->validate($token, $dataWithLeeway)); // false, because token can't be used before now() + 60, not within leeway

$dataWithLeeway->currentTime($now->modify('+51 seconds')); // changing the validation time to future

var_dump($validation->validate($token, $dataWithLeeway)); // true, because current time plus leeway is between "nbf" and "exp" claims

$dataWithLeeway->currentTime($now->modify('+3610 seconds')); // changing the validation time to future but within leeway

var_dump($validation->validate($token, $dataWithLeeway)); // true, because current time - 20 seconds leeway is less than exp

$dataWithLeeway->currentTime($now->modify('+4000 seconds')); // changing the validation time to future outside of leeway

var_dump($validation->validate($token, $dataWithLeeway)); // false, because token is expired since current time is greater than exp

重要

  • 您必须配置ValidationData来告知所有要验证的声明。
  • 如果ValidationData包含未在令牌中使用的声明或令牌包含在ValidationData中未配置的声明,则它们将由Token::validate()忽略。
  • expnbfiat声明默认在ValidationData::__construct()中配置为当前时间(DateTimeImmutable)。
  • ValidationData的可选$leeway参数将在验证基于时间的声明时使用该数量的秒数。对于“已发行”(iat)和“不在之前”(nbf)声明,它假装我们处于未来;对于“过期时间”(exp)声明,它假装我们处于过去。这允许出现发布服务器和验证服务器时钟不同的场景,如RFC 7519第4.1节中所述。

令牌签名

我们可以使用签名来验证令牌在生成后是否被修改。此库实现了HmacRSAECDSAEdDSABlake2b签名(使用256、384和512位)。none不是签名。

重要

不要允许发送到解析器的字符串决定要使用哪种签名算法,否则您的应用程序将容易受到关键的JWT安全漏洞的影响。

下面的示例是安全的,因为Signer的选择是硬编码的,不能被恶意用户影响。

Hmac和Blake2b

Hmac签名非常简单易用

use DateTimeImmutable;
use Larke\JWT\Builder;
use Larke\JWT\Validator;
use Larke\JWT\Signer\Hmac\Sha256;
use Larke\JWT\Signer\Key\InMemory;

$now    = new DateTimeImmutable();
$signer = new Sha256();
$key    = InMemory::plainText('testing');

$token = (new Builder())
    ->issuedBy('http://example.com') // Configures the issuer (iss claim)
    ->permittedFor('http://example.org') // Configures the audience (aud claim)
    ->identifiedBy('4f1g23a12aa', true) // Configures the id (jti claim), replicating as a header item
    ->issuedAt($now) // Configures the time that the token was issue (iat claim)
    ->canOnlyBeUsedAfter($now->modify('+1 minute')) // Configures the time that the token can be used (nbf claim)
    ->expiresAt($now->modify('+1 hour')) // Configures the expiration time of the token (exp claim)
    ->withClaim('uid', 1) // Configures a new claim, called "uid"
    ->getToken($signer, $key); // Retrieves the generated token

$key1 = InMemory::plainText('testing 1');
$key2 = InMemory::plainText('testing');

$validation = new Validator();

var_dump($validation->verify($token, $signer, $key1)); // false, because the key is different
var_dump($validation->verify($token, $signer, $key2)); // true, because the key is the same

RSA、ECDSA和EdDSA

RSA、ECDSA和EdDSA签名基于公钥和私钥,因此您必须使用私钥生成并使用公钥验证

use DateTimeImmutable;
use Larke\JWT\Builder;
use Larke\JWT\Validator;
use Larke\JWT\Signer\Key\LocalFileReference;
use Larke\JWT\Signer\Rsa\Sha256; // you can use Larke\JWT\Signer\Ecdsa\Sha256 if you're using ECDSA keys

$now        = new DateTimeImmutable();
$signer     = new Sha256();
$privateKey = LocalFileReference::file('file://{path to your private key}');

$token = (new Builder())
    ->issuedBy('http://example.com') // Configures the issuer (iss claim)
    ->permittedFor('http://example.org') // Configures the audience (aud claim)
    ->identifiedBy('4f1g23a12aa', true) // Configures the id (jti claim), replicating as a header item
    ->issuedAt($now) // Configures the time that the token was issue (iat claim)
    ->canOnlyBeUsedAfter($now->modify('+1 minute')) // Configures the time that the token can be used (nbf claim)
    ->expiresAt($now->modify('+1 hour')) // Configures the expiration time of the token (exp claim)
    ->withClaim('uid', 1) // Configures a new claim, called "uid"
    ->getToken($signer, $privateKey); // Retrieves the generated token

$publicKey = LocalFileReference::file('file://{path to your public key}');

$validation = new Validator();

var_dump($validation->verify($token, $signer, $publicKey)); // true when the public key was generated by the private one =)

重要的是要说明,如果您使用RSA密钥,则不应调用ECDSA签名者(反之亦然),否则sign()verify()将引发异常!