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

imi-jwt 组件兼容支持 PHP 8.0

上级 9d8f588e
......@@ -60,7 +60,7 @@
"test-mqtt": "tests/phpunit -c src/Components/mqtt/tests/phpunit.xml",
"test-smarty": "tests/phpunit -c src/Components/smarty/tests/phpunit.xml",
"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-amqp",
"composer test-kafka",
......
......@@ -75,7 +75,7 @@ $data = [
'memberId' => 19260817,
];
$token = JWT::getToken($data); // Token 对象
$tokenContent = $token->__toString(); // Token 字符串
$tokenContent = $token->toString(); // Token 字符串
```
指定名称:
......@@ -87,7 +87,7 @@ $data = [
'memberId' => 19260817,
];
$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){
// 可以针对该对象做一些操作
$builder->withClaim('aaa', 'bbb');
}); // Token 对象
$tokenContent = $token->__toString(); // Token 字符串
$tokenContent = $token->toString(); // Token 字符串
```
### 验证 Token
......@@ -114,7 +114,8 @@ use \Imi\JWT\Facade\JWT;
/** @var \Lcobucci\JWT\Token $token */
$token = JWT::parseToken($jwt); // 仅验证是否合法
// $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
$validationData = new \Lcobucci\JWT\ValidationData;
......
......@@ -4,7 +4,7 @@
"license": "MIT",
"description": "在 imi 框架中非常方便地接入 jwt",
"require": {
"lcobucci/jwt": "3.3.*"
"lcobucci/jwt": "^3.4.0|^4.1.0"
},
"require-dev": {},
"autoload": {
......
......@@ -17,6 +17,14 @@ use Imi\JWT\Exception\InvalidTokenException;
use Imi\RequestContext;
use Imi\Util\ClassObject;
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
......@@ -73,41 +81,84 @@ class JWTValidationAop
$token = $jwt->parseToken($jwtStr, $jwtValidation->name ?? $jwt->getDefault());
// 验证
$validationData = new \Lcobucci\JWT\ValidationData();
if (false !== $jwtValidation->id)
if (3 === $jwt->getJwtPackageVersion())
{
$validationData->setId($jwtValidation->id ?? $config->getId());
}
if (false !== $jwtValidation->issuer)
{
$validationData->setIssuer($jwtValidation->issuer ?? $config->getIssuer());
}
if (false !== $jwtValidation->audience)
{
$validationData->setAudience($jwtValidation->audience ?? $config->getAudience());
}
if (false !== $jwtValidation->subject)
{
$validationData->setSubject($jwtValidation->subject ?? $config->getSubject());
}
if (!$token->validate($validationData))
{
throw new InvalidTokenException();
$validationData = new \Lcobucci\JWT\ValidationData();
if (false !== $jwtValidation->id)
{
$validationData->setId($jwtValidation->id ?? $config->getId());
}
if (false !== $jwtValidation->issuer)
{
$validationData->setIssuer($jwtValidation->issuer ?? $config->getIssuer());
}
if (false !== $jwtValidation->audience)
{
$validationData->setAudience($jwtValidation->audience ?? $config->getAudience());
}
if (false !== $jwtValidation->subject)
{
$validationData->setSubject($jwtValidation->subject ?? $config->getSubject());
}
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);
}
}
if ($jwtValidation->tokenParam || $jwtValidation->dataParam)
else
{
$args = ClassObject::convertArgsToKV($class, $joinPoint->getMethod(), $joinPoint->getArgs());
if ($jwtValidation->tokenParam)
$config = $jwt->getConfig($jwtValidation->name);
$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[$jwtValidation->dataParam] = $data;
$args = ClassObject::convertArgsToKV($class, $joinPoint->getMethod(), $joinPoint->getArgs());
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);
......
......@@ -6,9 +6,12 @@ use Imi\Bean\Annotation\Bean;
use Imi\JWT\Exception\ConfigNotFoundException;
use Imi\JWT\Exception\InvalidTokenException;
use Imi\JWT\Model\JWTConfig;
use Imi\JWT\Util\Builder;
use Imi\JWT\Util\Parser;
use Lcobucci\JWT\Builder;
use Lcobucci\JWT\Configuration;
use Lcobucci\JWT\Parser;
use Lcobucci\JWT\Signer\Key\InMemory;
use Lcobucci\JWT\Token;
use Lcobucci\JWT\Validation\Constraint\SignedWith;
/**
* @Bean("JWT")
......@@ -104,37 +107,84 @@ class JWT
{
throw new ConfigNotFoundException('Must option the config @app.beans.JWT.list');
}
$builder = new Builder();
$time = time();
$builder->permittedFor($config->getAudience())
if (3 === $this->getJwtPackageVersion())
{
$builder = new Builder();
$time = time();
$builder->permittedFor($config->getAudience())
->relatedTo($config->getSubject())
->expiresAt($time + $config->getExpires())
->issuedBy($config->getIssuer())
->canOnlyBeUsedAfter($config->getNotBefore())
->identifiedBy($config->getId());
$issuedAt = $config->getIssuedAt();
if (true === $issuedAt)
{
$builder->issuedAt($time);
}
elseif (false !== $issuedAt)
{
$builder->issuedAt($issuedAt);
$issuedAt = $config->getIssuedAt();
if (true === $issuedAt)
{
$builder->issuedAt($time);
}
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);
}
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;
}
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.
*
......@@ -142,7 +192,7 @@ class JWT
* @param string|null $name
* @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
{
......@@ -154,7 +204,14 @@ class JWT
$config = $this->getConfig($name);
$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
* @param string $jwt
* @param string|null $name
*
* @return \Lcobucci\JWT\Token
* @return \Lcobucci\JWT\Token|\Lcobucci\JWT\UnencryptedToken
*/
public function parseToken(string $jwt, ?string $name = null): Token
{
$token = (new Parser())->parse($jwt);
$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();
$key = $config->getPublicKey();
if (!$token->verify($signer, $key))
......@@ -178,7 +239,28 @@ class JWT
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;
}
public function getJwtPackageVersion(): int
{
return class_exists(\Lcobucci\JWT\Token\Parser::class) ? 4 : 3;
}
}
......@@ -13,9 +13,10 @@ use Imi\Facade\BaseFacade;
* @method static string|null getDefault()
* @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\Token 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\Token parseToken(string $jwt, string|null $name = NULL)
* @method static \Lcobucci\JWT\Token|\Lcobucci\JWT\UnencryptedToken getToken(mixed $data, string|null $name = NULL, callable|null $beforeGetToken = NULL)
* @method static \Lcobucci\JWT\Parser getParserInstance(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
{
......
......@@ -66,7 +66,7 @@ class JWTConfig
*
* @var int
*/
private $notBefore;
private $notBefore = 0;
/**
* JWT 发出时间
......@@ -238,7 +238,7 @@ class JWTConfig
*
* @return string
*/
public function getDataName()
public function getDataName(): string
{
return $this->dataName;
}
......@@ -248,7 +248,7 @@ class JWTConfig
*
* @return int
*/
public function getNotBefore()
public function getNotBefore(): int
{
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
'memberId' => 19260817,
];
$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);
$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.
先完成此消息的编辑!
想要评论请 注册