提交 297f5f1b 编写于 作者: weixin_47267244's avatar weixin_47267244

imi-jwt 组件兼容支持 PHP 8.0

上级 9d8f588e
...@@ -60,7 +60,7 @@ ...@@ -60,7 +60,7 @@
"test-mqtt": "tests/phpunit -c src/Components/mqtt/tests/phpunit.xml", "test-mqtt": "tests/phpunit -c src/Components/mqtt/tests/phpunit.xml",
"test-smarty": "tests/phpunit -c src/Components/smarty/tests/phpunit.xml", "test-smarty": "tests/phpunit -c src/Components/smarty/tests/phpunit.xml",
"test-components": [ "test-components": [
"(php -r \"exit(version_compare(PHP_VERSION, '8.0', '<') ? 0 : 1);\" && composer test-jwt) || echo 'skip jwt'", "composer test-jwt",
"composer test-queue", "composer test-queue",
"composer test-amqp", "composer test-amqp",
"composer test-kafka", "composer test-kafka",
......
...@@ -75,7 +75,7 @@ $data = [ ...@@ -75,7 +75,7 @@ $data = [
'memberId' => 19260817, 'memberId' => 19260817,
]; ];
$token = JWT::getToken($data); // Token 对象 $token = JWT::getToken($data); // Token 对象
$tokenContent = $token->__toString(); // Token 字符串 $tokenContent = $token->toString(); // Token 字符串
``` ```
指定名称: 指定名称:
...@@ -87,7 +87,7 @@ $data = [ ...@@ -87,7 +87,7 @@ $data = [
'memberId' => 19260817, 'memberId' => 19260817,
]; ];
$token = JWT::getToken($data, 'a'); // Token 对象 $token = JWT::getToken($data, 'a'); // Token 对象
$tokenContent = $token->__toString(); // Token 字符串 $tokenContent = $token->toString(); // Token 字符串
``` ```
自定义处理: 自定义处理:
...@@ -102,7 +102,7 @@ $token = JWT::getToken($data, 'a', function(\Lcobucci\JWT\Builder $builder){ ...@@ -102,7 +102,7 @@ $token = JWT::getToken($data, 'a', function(\Lcobucci\JWT\Builder $builder){
// 可以针对该对象做一些操作 // 可以针对该对象做一些操作
$builder->withClaim('aaa', 'bbb'); $builder->withClaim('aaa', 'bbb');
}); // Token 对象 }); // Token 对象
$tokenContent = $token->__toString(); // Token 字符串 $tokenContent = $token->toString(); // Token 字符串
``` ```
### 验证 Token ### 验证 Token
...@@ -114,7 +114,8 @@ use \Imi\JWT\Facade\JWT; ...@@ -114,7 +114,8 @@ use \Imi\JWT\Facade\JWT;
/** @var \Lcobucci\JWT\Token $token */ /** @var \Lcobucci\JWT\Token $token */
$token = JWT::parseToken($jwt); // 仅验证是否合法 $token = JWT::parseToken($jwt); // 仅验证是否合法
// $token = JWT::parseToken($jwt, 'a'); // 指定配置名称 // $token = JWT::parseToken($jwt, 'a'); // 指定配置名称
$data = $token->getClaim('data'); // 获取往token里丢的数据 $data = $token->getClaim('data'); // 获取往token里丢的数据,PHP <= 7.3
$data = $token->claim()->get('data'); // 获取往token里丢的数据,PHP >= 7.4
// 验证有效期、id、issuer、audience、subject // 验证有效期、id、issuer、audience、subject
$validationData = new \Lcobucci\JWT\ValidationData; $validationData = new \Lcobucci\JWT\ValidationData;
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
"license": "MIT", "license": "MIT",
"description": "在 imi 框架中非常方便地接入 jwt", "description": "在 imi 框架中非常方便地接入 jwt",
"require": { "require": {
"lcobucci/jwt": "3.3.*" "lcobucci/jwt": "^3.4.0|^4.1.0"
}, },
"require-dev": {}, "require-dev": {},
"autoload": { "autoload": {
......
...@@ -17,6 +17,14 @@ use Imi\JWT\Exception\InvalidTokenException; ...@@ -17,6 +17,14 @@ use Imi\JWT\Exception\InvalidTokenException;
use Imi\RequestContext; use Imi\RequestContext;
use Imi\Util\ClassObject; use Imi\Util\ClassObject;
use Imi\Util\Http\Consts\RequestHeader; use Imi\Util\Http\Consts\RequestHeader;
use Lcobucci\Clock\FrozenClock;
use Lcobucci\JWT\Configuration;
use Lcobucci\JWT\Signer\Key\InMemory;
use Lcobucci\JWT\Validation\Constraint\IdentifiedBy;
use Lcobucci\JWT\Validation\Constraint\IssuedBy;
use Lcobucci\JWT\Validation\Constraint\LooseValidAt;
use Lcobucci\JWT\Validation\Constraint\PermittedFor;
use Lcobucci\JWT\Validation\Constraint\RelatedTo;
/** /**
* @Aspect * @Aspect
...@@ -73,41 +81,84 @@ class JWTValidationAop ...@@ -73,41 +81,84 @@ class JWTValidationAop
$token = $jwt->parseToken($jwtStr, $jwtValidation->name ?? $jwt->getDefault()); $token = $jwt->parseToken($jwtStr, $jwtValidation->name ?? $jwt->getDefault());
// 验证 // 验证
$validationData = new \Lcobucci\JWT\ValidationData(); if (3 === $jwt->getJwtPackageVersion())
if (false !== $jwtValidation->id)
{ {
$validationData->setId($jwtValidation->id ?? $config->getId()); $validationData = new \Lcobucci\JWT\ValidationData();
} if (false !== $jwtValidation->id)
if (false !== $jwtValidation->issuer) {
{ $validationData->setId($jwtValidation->id ?? $config->getId());
$validationData->setIssuer($jwtValidation->issuer ?? $config->getIssuer()); }
} if (false !== $jwtValidation->issuer)
if (false !== $jwtValidation->audience) {
{ $validationData->setIssuer($jwtValidation->issuer ?? $config->getIssuer());
$validationData->setAudience($jwtValidation->audience ?? $config->getAudience()); }
} if (false !== $jwtValidation->audience)
if (false !== $jwtValidation->subject) {
{ $validationData->setAudience($jwtValidation->audience ?? $config->getAudience());
$validationData->setSubject($jwtValidation->subject ?? $config->getSubject()); }
} if (false !== $jwtValidation->subject)
if (!$token->validate($validationData)) {
{ $validationData->setSubject($jwtValidation->subject ?? $config->getSubject());
throw new InvalidTokenException(); }
if (!$token->validate($validationData))
{
throw new InvalidTokenException();
}
if ($jwtValidation->tokenParam || $jwtValidation->dataParam)
{
$args = ClassObject::convertArgsToKV($class, $joinPoint->getMethod(), $joinPoint->getArgs());
if ($jwtValidation->tokenParam)
{
$args[$jwtValidation->tokenParam] = $token;
}
if ($jwtValidation->dataParam)
{
$data = $token->getClaim($config->getDataName());
$args[$jwtValidation->dataParam] = $data;
}
$args = array_values($args);
}
} }
else
if ($jwtValidation->tokenParam || $jwtValidation->dataParam)
{ {
$args = ClassObject::convertArgsToKV($class, $joinPoint->getMethod(), $joinPoint->getArgs()); $config = $jwt->getConfig($jwtValidation->name);
if ($jwtValidation->tokenParam) $configuration = Configuration::forAsymmetricSigner($config->getSignerInstance(), InMemory::plainText($config->getPrivateKey()), InMemory::plainText($config->getPublicKey()));
$constraints = [];
if (false !== $jwtValidation->id && null !== ($id = ($jwtValidation->id ?? $config->getId())))
{
$constraints[] = new IdentifiedBy($id);
}
if (false !== $jwtValidation->issuer && null !== ($issuer = ($jwtValidation->issuer ?? $config->getIssuer())))
{
$constraints[] = new IssuedBy($issuer);
}
if (false !== $jwtValidation->audience && null !== ($audience = ($jwtValidation->audience ?? $config->getAudience())))
{
$constraints[] = new PermittedFor($audience);
}
if (false !== $jwtValidation->subject && null !== ($subject = ($jwtValidation->subject ?? $config->getSubject())))
{
$constraints[] = new RelatedTo($subject);
}
$constraints[] = new LooseValidAt(new FrozenClock(new \DateTimeImmutable()));
if ($constraints && !$configuration->validator()->validate($token, ...$constraints))
{ {
$args[$jwtValidation->tokenParam] = $token; throw new InvalidTokenException();
} }
if ($jwtValidation->dataParam) if ($jwtValidation->tokenParam || $jwtValidation->dataParam)
{ {
$data = $token->getClaim($config->getDataName()); $args = ClassObject::convertArgsToKV($class, $joinPoint->getMethod(), $joinPoint->getArgs());
$args[$jwtValidation->dataParam] = $data; if ($jwtValidation->tokenParam)
{
$args[$jwtValidation->tokenParam] = $token;
}
if ($jwtValidation->dataParam)
{
$data = $token->claims()->get($config->getDataName());
$args[$jwtValidation->dataParam] = $data;
}
$args = array_values($args);
} }
$args = array_values($args);
} }
return $joinPoint->proceed($args ?? null); return $joinPoint->proceed($args ?? null);
......
...@@ -6,9 +6,12 @@ use Imi\Bean\Annotation\Bean; ...@@ -6,9 +6,12 @@ use Imi\Bean\Annotation\Bean;
use Imi\JWT\Exception\ConfigNotFoundException; use Imi\JWT\Exception\ConfigNotFoundException;
use Imi\JWT\Exception\InvalidTokenException; use Imi\JWT\Exception\InvalidTokenException;
use Imi\JWT\Model\JWTConfig; use Imi\JWT\Model\JWTConfig;
use Imi\JWT\Util\Builder; use Lcobucci\JWT\Builder;
use Imi\JWT\Util\Parser; use Lcobucci\JWT\Configuration;
use Lcobucci\JWT\Parser;
use Lcobucci\JWT\Signer\Key\InMemory;
use Lcobucci\JWT\Token; use Lcobucci\JWT\Token;
use Lcobucci\JWT\Validation\Constraint\SignedWith;
/** /**
* @Bean("JWT") * @Bean("JWT")
...@@ -104,37 +107,84 @@ class JWT ...@@ -104,37 +107,84 @@ class JWT
{ {
throw new ConfigNotFoundException('Must option the config @app.beans.JWT.list'); throw new ConfigNotFoundException('Must option the config @app.beans.JWT.list');
} }
$builder = new Builder(); if (3 === $this->getJwtPackageVersion())
$time = time(); {
$builder->permittedFor($config->getAudience()) $builder = new Builder();
$time = time();
$builder->permittedFor($config->getAudience())
->relatedTo($config->getSubject()) ->relatedTo($config->getSubject())
->expiresAt($time + $config->getExpires()) ->expiresAt($time + $config->getExpires())
->issuedBy($config->getIssuer()) ->issuedBy($config->getIssuer())
->canOnlyBeUsedAfter($config->getNotBefore()) ->canOnlyBeUsedAfter($config->getNotBefore())
->identifiedBy($config->getId()); ->identifiedBy($config->getId());
$issuedAt = $config->getIssuedAt(); $issuedAt = $config->getIssuedAt();
if (true === $issuedAt) if (true === $issuedAt)
{ {
$builder->issuedAt($time); $builder->issuedAt($time);
} }
elseif (false !== $issuedAt) elseif (false !== $issuedAt)
{ {
$builder->issuedAt($issuedAt); $builder->issuedAt($issuedAt);
}
if ($headers = $config->getHeaders())
{
foreach ($headers as $k => $v)
{
$builder->withHeader($k, $v);
}
}
$signer = $config->getSignerInstance();
$key = $config->getPrivateKey();
$builder->sign($signer, $key);
} }
if ($headers = $config->getHeaders()) else
{ {
foreach ($headers as $k => $v) $configuration = Configuration::forAsymmetricSigner($config->getSignerInstance(), InMemory::plainText($config->getPrivateKey()), InMemory::plainText($config->getPublicKey()));
$builder = $configuration->builder();
$now = new \DateTimeImmutable();
$builder->permittedFor($config->getAudience())
->relatedTo($config->getSubject())
->expiresAt($now->modify('+' . ($config->getExpires() ?? 0) . ' second'))
->issuedBy($config->getIssuer())
->canOnlyBeUsedAfter($now->modify('+' . $config->getNotBefore() . ' second'))
->identifiedBy($config->getId() ?? '');
$issuedAt = $config->getIssuedAt();
if (true === $issuedAt)
{ {
$builder->withHeader($k, $v); $builder->issuedAt($now);
}
elseif (false !== $issuedAt)
{
$builder->issuedAt($issuedAt);
}
if ($headers = $config->getHeaders())
{
foreach ($headers as $k => $v)
{
$builder->withHeader($k, $v);
}
} }
} }
$signer = $config->getSignerInstance();
$key = $config->getPrivateKey();
$builder->sign($signer, $key);
return $builder; return $builder;
} }
public function getParserInstance(?string $name = null): Parser
{
if (3 === $this->getJwtPackageVersion())
{
return new \Lcobucci\JWT\Parser();
}
else
{
$config = $this->getConfig($name);
$configuration = Configuration::forAsymmetricSigner($config->getSignerInstance(), InMemory::plainText($config->getPrivateKey()), InMemory::plainText($config->getPublicKey()));
return $configuration->parser();
}
}
/** /**
* 生成 Token. * 生成 Token.
* *
...@@ -142,7 +192,7 @@ class JWT ...@@ -142,7 +192,7 @@ class JWT
* @param string|null $name * @param string|null $name
* @param callable|null $beforeGetToken * @param callable|null $beforeGetToken
* *
* @return \Lcobucci\JWT\Token * @return \Lcobucci\JWT\Token|\Lcobucci\JWT\UnencryptedToken
*/ */
public function getToken($data, ?string $name = null, ?callable $beforeGetToken = null): Token public function getToken($data, ?string $name = null, ?callable $beforeGetToken = null): Token
{ {
...@@ -154,7 +204,14 @@ class JWT ...@@ -154,7 +204,14 @@ class JWT
$config = $this->getConfig($name); $config = $this->getConfig($name);
$builder->withClaim($config->getDataName(), $data); $builder->withClaim($config->getDataName(), $data);
return $builder->getToken(); if (3 === $this->getJwtPackageVersion())
{
return $builder->getToken();
}
else
{
return $builder->getToken($config->getSignerInstance(), InMemory::plainText($config->getPrivateKey()));
}
} }
/** /**
...@@ -163,14 +220,18 @@ class JWT ...@@ -163,14 +220,18 @@ class JWT
* @param string $jwt * @param string $jwt
* @param string|null $name * @param string|null $name
* *
* @return \Lcobucci\JWT\Token * @return \Lcobucci\JWT\Token|\Lcobucci\JWT\UnencryptedToken
*/ */
public function parseToken(string $jwt, ?string $name = null): Token public function parseToken(string $jwt, ?string $name = null): Token
{ {
$token = (new Parser())->parse($jwt);
$config = $this->getConfig($name); $config = $this->getConfig($name);
if ($config) if (!$config)
{
throw new InvalidTokenException();
}
if (3 === $this->getJwtPackageVersion())
{ {
$token = (new \Lcobucci\JWT\Parser())->parse($jwt);
$signer = $config->getSignerInstance(); $signer = $config->getSignerInstance();
$key = $config->getPublicKey(); $key = $config->getPublicKey();
if (!$token->verify($signer, $key)) if (!$token->verify($signer, $key))
...@@ -178,7 +239,28 @@ class JWT ...@@ -178,7 +239,28 @@ class JWT
throw new InvalidTokenException(); throw new InvalidTokenException();
} }
} }
else
{
$parser = $this->getParserInstance($name);
$token = $parser->parse($jwt);
$signer = $config->getSignerInstance();
$key = $config->getPublicKey();
$signedWith = new SignedWith($signer, InMemory::plainText($key));
try
{
$signedWith->assert($token);
}
catch (\Throwable $th)
{
throw new InvalidTokenException($th->getMessage(), $th->getCode(), $th->getPrevious());
}
}
return $token; return $token;
} }
public function getJwtPackageVersion(): int
{
return class_exists(\Lcobucci\JWT\Token\Parser::class) ? 4 : 3;
}
} }
...@@ -13,9 +13,10 @@ use Imi\Facade\BaseFacade; ...@@ -13,9 +13,10 @@ use Imi\Facade\BaseFacade;
* @method static string|null getDefault() * @method static string|null getDefault()
* @method static \Imi\JWT\Model\JWTConfig|null getConfig(string|null $name = NULL) * @method static \Imi\JWT\Model\JWTConfig|null getConfig(string|null $name = NULL)
* @method static \Lcobucci\JWT\Builder getBuilderInstance(string|null $name = NULL) * @method static \Lcobucci\JWT\Builder getBuilderInstance(string|null $name = NULL)
* @method static \Lcobucci\JWT\Token getToken(mixed $data, string|null $name = NULL, callable|null $beforeGetToken = NULL) * @method static \Lcobucci\JWT\Token|\Lcobucci\JWT\UnencryptedToken getToken(mixed $data, string|null $name = NULL, callable|null $beforeGetToken = NULL)
* @method static \Imi\JWT\Util\Parser getParserInstance(string|null $name = NULL) * @method static \Lcobucci\JWT\Parser getParserInstance(string|null $name = NULL)
* @method static \Lcobucci\JWT\Token parseToken(string $jwt, string|null $name = NULL) * @method static \Lcobucci\JWT\Token|\Lcobucci\JWT\UnencryptedToken parseToken(string $jwt, string|null $name = NULL)
* @method static int getJwtPackageVersion()
*/ */
abstract class JWT extends BaseFacade abstract class JWT extends BaseFacade
{ {
......
...@@ -66,7 +66,7 @@ class JWTConfig ...@@ -66,7 +66,7 @@ class JWTConfig
* *
* @var int * @var int
*/ */
private $notBefore; private $notBefore = 0;
/** /**
* JWT 发出时间 * JWT 发出时间
...@@ -238,7 +238,7 @@ class JWTConfig ...@@ -238,7 +238,7 @@ class JWTConfig
* *
* @return string * @return string
*/ */
public function getDataName() public function getDataName(): string
{ {
return $this->dataName; return $this->dataName;
} }
...@@ -248,7 +248,7 @@ class JWTConfig ...@@ -248,7 +248,7 @@ class JWTConfig
* *
* @return int * @return int
*/ */
public function getNotBefore() public function getNotBefore(): int
{ {
return $this->notBefore; return $this->notBefore;
} }
......
<?php
namespace Imi\JWT\Util;
class Builder extends \Lcobucci\JWT\Builder
{
}
<?php
namespace Imi\JWT\Util;
class Parser extends \Lcobucci\JWT\Parser
{
}
...@@ -18,12 +18,26 @@ class JWTTest extends TestCase ...@@ -18,12 +18,26 @@ class JWTTest extends TestCase
'memberId' => 19260817, 'memberId' => 19260817,
]; ];
$token = JWT::getToken($data, null, function (Builder $builder) { $token = JWT::getToken($data, null, function (Builder $builder) {
$builder->expiresAt(strtotime('1926-08-17')); if (3 === JWT::getJwtPackageVersion())
{
$builder->expiresAt(strtotime('1926-08-17'));
}
else
{
$builder->expiresAt(new \DateTimeImmutable('1926-08-17'));
}
}); });
$tokenStr = (string) $token; $tokenStr = $token->toString();
$token2 = JWT::parseToken($tokenStr); $token2 = JWT::parseToken($tokenStr);
$config = JWT::getConfig(); $config = JWT::getConfig();
$this->assertEquals(json_encode($data), json_encode($token2->getClaim($config->getDataName()))); if (3 === JWT::getJwtPackageVersion())
{
$this->assertEquals(json_encode($data), json_encode($token2->getClaim($config->getDataName())));
}
else
{
$this->assertEquals(json_encode($data), json_encode($token2->claims()->get($config->getDataName())));
}
} }
/** /**
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册