brainpicker/reallysimplejwt

一个用于生成用户认证JSON Web Tokens的简单库。

2.0.4 2019-05-22 08:39 UTC

README

一个简单的PHP库,用于创建使用HMAC SHA256进行签名签名的JSON Web Tokens。对于基本用法,库通过静态接口暴露,允许开发人员创建存储用户标识和过期时间的令牌。

库还支持扩展,开发人员可以定义自己的编码标准,设置所有RFC标准 JWT声明,并设置自己的私有声明。

注意:为PHP 5.6快速适配,未进行测试以验证

内容

什么是JSON Web Token?

JSON Web Tokens是一种创建关于用户或系统的声明的URL友好访问令牌的标准。

令牌分为三部分:头部、有效负载和签名;各部分之间用点分隔。每一部分都使用base64url标准进行编码,请参阅RFC

一个JWT示例

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

头部和有效负载都是包含多个声明的编码JSON字符串

// Header
{
  "alg": "HS256",
  "typ": "JWT"
}

// Payload
{
  "sub": "1234567890",
  "name": "John Doe",
  "iat": 1516239022
}

声明是一个键值对,例如 "typ": "JWT",请阅读RFC 7519 了解更多关于JSON Web Token声明的信息。

通过签名实现令牌安全,签名由头部、有效负载和只有令牌作者知道的秘密组成。这些信息被散列,然后使用base64url进行编码。

如果恶意用户尝试编辑头部或有效负载声明,只要使用强密钥,他们将无法复制签名。有关更多信息,请参阅令牌安全

设置

通过命令行Composer

composer require rbdwllr/reallysimplejwt

通过composer.json

"require": {
    "rbdwllr/reallysimplejwt": "^2.0"
}

基本用法

对于基本用法,库通过ReallySimpleJWT\Token类暴露一组静态方法,允许开发人员创建和验证基本的JSON Web Tokens。

创建令牌

调用create()静态方法,并传递用户标识、密钥、过期日期时间数字和令牌发行者。

如果成功,将返回一个令牌字符串,如果失败,将抛出ReallySimpleJWT\Exception\ValidateException异常。

use ReallySimpleJWT\Token;

$userId = 12;
$secret = 'sec!ReT423*&';
$expiration = time() + 3600;
$issuer = 'localhost';

$token = Token::create($userId, $secret, $expiration, $issuer);

要创建更定制的令牌,开发人员可以使用customPayload()方法。这允许根据表示有效负载声明的键值对数组创建令牌。

use ReallySimpleJWT\Token;

$payload = [
    'iat' => time(),
    'uid' => 1,
    'exp' => time() + 10,
    'iss' => 'localhost'
];

$secret = 'Hello&MikeFooBar123';

$token = Token::customPayload($payload, $secret);

如果成功,customPayload()方法将返回一个JWT令牌字符串,如果失败,将抛出异常。

验证令牌

要验证JSON Web Token,请调用validate()静态方法,并传递令牌字符串和密钥。验证方法检查令牌结构是否正确、签名是否有效、过期时间是否已过期以及不早于时间是否已过。

如果成功,它将返回true,如果失败,它将返回false。

use ReallySimpleJWT\Token;

$token = 'aaa.bbb.ccc';
$secret = 'sec!ReT423*&'

$result = Token::validate($token, $secret);

获取头和有效负载声明数据

要检索头或有效负载的令牌声明数据,请调用getHeader()和/或getPayload()静态方法。

这两个方法在成功时将返回关联数组,在失败时将抛出异常。

use ReallySimpleJWT\Token;

$token = 'aaa.bbb.ccc';
$secret = 'sec!ReT423*&'

// Return the header claims
Token::getHeader($token, $secret);

// Return the payload claims
Token::getPayload($token, $secret);

工厂方法

ReallySimpleJWT\Token 类还提供了两个工厂方法,用于访问核心的 ReallySimpleJWT\BuildReallySimpleJWT\Parse 类。

Token::builder(); // Returns an instance of ReallySimpleJWT\Build

Token::parser($token, $secret); // Returns an instance of ReallySimpleJWT\Parse

高级用法

要创建自定义的 JSON Web Tokens,开发人员需要直接访问 ReallySimpleJWT\BuildReallySimpleJWT\Parse 类。

创建自定义令牌

ReallySimpleJWT\Build 类允许您创建一个完全独特的 JSON Web Token。它提供了所有由 RFC 定义的头部和有效载荷声明的辅助方法。例如,setIssuer() 方法将添加 iss 声明到令牌有效载荷。

该类还允许开发人员通过 setHeaderClaim()setPayloadClaim() 方法设置自定义的头部和有效载荷声明。

可以将这些方法链接在一起,当调用 build() 方法时,令牌将在 ReallySimpleJWT\Jwt 对象中生成并返回。

use ReallySimpleJWT\Build;
use ReallySimpleJWT\Validate;
use ReallySimpleJWT\Encode;

$build = new Build('JWT', new Validate(), new Encode());

$token = $build->setContentType('JWT')
    ->setHeaderClaim('info', 'foo')
    ->setSecret('!secReT$123*')
    ->setIssuer('localhost')
    ->setSubject('admins')
    ->setAudience('https://google.com')
    ->setExpiration(time() + 30)
    ->setNotBefore(time() - 30)
    ->setIssuedAt(time())
    ->setJwtId('123ABC')
    ->setPayloadClaim('uid', 12)
    ->build();

访问令牌

当开发人员在对 ReallySimpleJWT\Build 类调用 build() 方法时,将返回一个 ReallySimpleJWT\Jwt 对象。Jwt 类提供了两个方法 getToken()getSecret()。前者返回生成的 JSON Web Token,后者返回用于令牌签名的密钥。

要通过 ReallySimpleJWT\Parse 类解析 JSON Web Token,开发人员必须首先通过注入令牌和密钥创建一个新的 ReallySimpleJWT\Jwt 对象。

use ReallySimpleJWT\Jwt;

$token = 'aaa.bbb.ccc';
$secret = '!secReT$123*';

$jwt = new Jwt($token, $secret);

// Return the token
$jwt->getToken();

// Return the secret
$jwt->getSecret();

解析和验证令牌

ReallySimpleJWT\Parse 类允许开发人员解析和验证 JSON Web Token。有三个验证方法可用,它们都可以链接使用。

  • validate() 确认令牌的结构和签名的有效性。
  • validateExpiration() 确认令牌过期声明(exp)尚未过期。
  • validateNotBefore() 确认令牌不早于声明(nbf)已过。

如果提供的令牌有任何问题,每个验证方法都将抛出 ReallySimpleJWT\Exception\ValidateException

完成验证后应调用 parse() 方法,它将解码 JSON Web Token。然后,它将作为 ReallySimpleJWT\Parsed 对象返回结果。这将提供访问令牌在头部和有效载荷中持有的声明数据。

use ReallySimpleJWT\Parse;
use ReallySimpleJWT\Jwt;
use ReallySimpleJWT\Validate;
use ReallySimpleJWT\Encode;

$token = 'aaa.bbb.ccc';
$secret = '!secReT$123*';

$jwt = new Jwt($token, $secret);

$parse = new Parse($jwt, new Validate(), new Encode());

$parsed = $parse->validate()
    ->validateExpiration()
    ->validateNotBefore()
    ->parse();

// Return the token header claims as an associative array.
$parsed->getHeader();

// Return the token payload claims as an associative array.
$parsed->getPayload();

访问令牌声明数据

当开发人员在对 ReallySimpleJWT\Parse 类调用 parse() 方法时,将返回 ReallySimpleJWT\Parsed 类。

它提供了许多辅助方法来获取令牌声明数据。开发人员可以调用 getHeader()getPayload() 方法来获取相应的声明数据作为关联数组。

或者,开发人员可以调用一个符合 RFC 的声明方法之一

头部

  • getAlgorithm()
  • getType()
  • getContentType()

有效载荷

  • getIssuer()
  • getSubject()
  • getAudience()
  • getExpiration()
  • getNotBefore()
  • getIssuedAt()
  • getJwtId()

自定义编码

默认情况下,该库通过使用 sha256 算法通过 hash_hmac() 对 JWT 签名进行哈希和编码。如果开发人员想要使用自定义的编码形式,只需生成一个符合 ReallySimpleJWT\Interfaces\EncodeInterface 的自定义编码类。

interface EncodeInterface
{
    public function getAlgorithm(): string;

    public function encode(string $toEncode): string;

    public function decode(string $toDecode): string;

    public function signature(string $header, string $payload, string $secret): string;
}

错误消息和代码

在创建和解析 JWT 令牌的许多情况下,ReallySimpleJWT 库将抛出异常以突出显示问题。以下是错误代码、消息及其解释。

* 不再使用,库将不会生成此错误代码。

令牌安全

JWT(JSON Web Token)标准RFC 7519允许创建不带签名和带安全/哈希签名的令牌。然而,ReallySimpleJWT库默认强制执行安全性,因为没有任何逻辑理由不这样做。所有创建的令牌都必须有签名和一个强密码,但库将验证不带密码或强密码的令牌。该库将不验证不带签名的令牌。

可以通过创建一个实现了ReallySimpleJWT\Interfaces\EncodeInterface的自定义编码类来编辑和增强签名及其安全性级别。请参阅自定义编码部分。

签名密钥

此JWT库强制实施严格的密码安全措施,如下所示:密码长度至少为12个字符;包含数字;大小写字母;以及以下特殊字符之一:*&!@%^#$

// Bad Secret
secret123

// Good Secret
sec!ReT423*&

这是因为有很多JWT破解工具,这意味着弱密码很容易被破解,从而使JWT提供的安全性变得无用。

第一版支持

本库的第一个版本支持将持续到2019年7月。该版本不会添加新功能,只会修复bug和安全补丁。

许可证

MIT

作者

Rob Waller

Twitter: @robdwaller