placetopay/reallysimplejwt

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

2.1.0 2019-06-05 22:09 UTC

README

这是一个简单的PHP库,用于创建使用HMAC SHA256签名签名的JSON Web Tokens。对于基本使用,该库公开了一个静态接口,允许开发者创建存储用户标识和过期时间的令牌。

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

内容

什么是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 placetopay/reallysimplejwt

通过composer.json

"require": {
    "placetopay/reallysimplejwt": "^2.1"
}

基本用法

对于基本用法,该库通过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库不强制实施严格的密钥字符串安全性。然而,设置一个严格的模式来验证密钥是一个好的实践。

# setting a secret string pattern for validation
\ReallySimpleJWT\Validate::$secretPattern = '/^.*$/';

// Same validation without special characters
\ReallySimpleJWT\Validate::$secretPattern = /^.*(?=.{12,}+)(?=.*[0-9]+)(?=.*[A-Z]+)(?=.*[a-z]+).*$/;

// Bad Secret
secret123

// Good Secret
sec!ReT423*&

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

许可证

MIT

作者

Rob Waller

Twitter: @robdwaller