mirror of
https://gitee.com/ctexthuang/hyperf_rbac_framework_server_ctexthuang.git
synced 2025-12-25 17:07:49 +08:00
first commit
This commit is contained in:
233
app/Lib/Jwt/AbstractJwt.php
Normal file
233
app/Lib/Jwt/AbstractJwt.php
Normal file
@@ -0,0 +1,233 @@
|
||||
<?php
|
||||
|
||||
namespace App\Lib\Jwt;
|
||||
|
||||
use App\Interface\JwtInterface;
|
||||
use Carbon\Carbon;
|
||||
use Hyperf\Cache\CacheManager;
|
||||
use Hyperf\Cache\Driver\DriverInterface;
|
||||
use Hyperf\Collection\Arr;
|
||||
use Lcobucci\JWT\Builder;
|
||||
use Lcobucci\JWT\JwtFacade;
|
||||
use Lcobucci\JWT\Signer;
|
||||
use Lcobucci\JWT\Signer\Key;
|
||||
use Lcobucci\JWT\UnencryptedToken;
|
||||
use Lcobucci\JWT\Validation\Constraint;
|
||||
use Lcobucci\JWT\Validation\Constraint\SignedWith;
|
||||
use Lcobucci\JWT\Validation\Constraint\StrictValidAt;
|
||||
use Psr\SimpleCache\InvalidArgumentException;
|
||||
|
||||
abstract class AbstractJwt implements JwtInterface
|
||||
{
|
||||
/**
|
||||
* @param array $config
|
||||
* @param CacheManager $cacheManager
|
||||
* @param Clock $clock
|
||||
* @param AccessTokenConstraint $accessTokenConstraint
|
||||
* @param RefreshTokenConstraint $refreshTokenConstraint
|
||||
*/
|
||||
public function __construct(
|
||||
private readonly array $config,
|
||||
private readonly CacheManager $cacheManager,
|
||||
private readonly Clock $clock,
|
||||
private readonly AccessTokenConstraint $accessTokenConstraint,
|
||||
private readonly RefreshTokenConstraint $refreshTokenConstraint
|
||||
) {}
|
||||
|
||||
/**
|
||||
* @param string $sub
|
||||
* @param \Closure|null $callable
|
||||
* @return UnencryptedToken
|
||||
*/
|
||||
public function builderAccessToken(string $sub, ?\Closure $callable = null): UnencryptedToken
|
||||
{
|
||||
return $this->getJwtFacade()->issue(
|
||||
$this->getSigner(),
|
||||
$this->getSigningKey(),
|
||||
function (Builder $builder, \DateTimeImmutable $immutable) use ($sub, $callable) {
|
||||
$builder = $builder->identifiedBy($sub);
|
||||
if ($callable !== null) {
|
||||
$builder = $callable($builder);
|
||||
}
|
||||
return $builder->expiresAt($this->getExpireAt($immutable));
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sub
|
||||
* @param \Closure|null $callable
|
||||
* @return UnencryptedToken
|
||||
*/
|
||||
public function builderRefreshToken(string $sub, ?\Closure $callable = null): UnencryptedToken
|
||||
{
|
||||
return $this->getJwtFacade()->issue(
|
||||
$this->getSigner(),
|
||||
$this->getSigningKey(),
|
||||
function (Builder $builder, \DateTimeImmutable $immutable) use ($sub, $callable) {
|
||||
$builder = $builder->identifiedBy($sub);
|
||||
$builder = $builder->expiresAt($this->getRefreshExpireAt($immutable));
|
||||
if ($callable !== null) {
|
||||
$builder = $callable($builder);
|
||||
}
|
||||
return $builder->relatedTo('refresh');
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $accessToken
|
||||
* @return UnencryptedToken
|
||||
*/
|
||||
public function parserAccessToken(string $accessToken): UnencryptedToken
|
||||
{
|
||||
return $this->getJwtFacade()
|
||||
->parse(
|
||||
$accessToken,
|
||||
new SignedWith(
|
||||
$this->getSigner(),
|
||||
$this->getSigningKey()
|
||||
),
|
||||
new StrictValidAt(
|
||||
$this->clock,
|
||||
$this->clock->now()->diff($this->getExpireAt($this->clock->now()))
|
||||
),
|
||||
$this->getBlackListConstraint(),
|
||||
$this->refreshTokenConstraint
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $refreshToken
|
||||
* @return UnencryptedToken
|
||||
*/
|
||||
public function parserRefreshToken(string $refreshToken): UnencryptedToken
|
||||
{
|
||||
return $this->getJwtFacade()
|
||||
->parse(
|
||||
$refreshToken,
|
||||
new SignedWith(
|
||||
$this->getSigner(),
|
||||
$this->getSigningKey()
|
||||
),
|
||||
new StrictValidAt(
|
||||
$this->clock,
|
||||
$this->clock->now()->diff($this->getRefreshExpireAt($this->clock->now()))
|
||||
),
|
||||
$this->getBlackListConstraint(),
|
||||
$this->accessTokenConstraint
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param UnencryptedToken $token
|
||||
* @return bool
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function addBlackList(UnencryptedToken $token): bool
|
||||
{
|
||||
return $this->getCacheDriver()->set($token->toString(), 1, $this->getBlackConfig('ttl', 600));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param UnencryptedToken $token
|
||||
* @return bool
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function hasBlackList(UnencryptedToken $token): bool
|
||||
{
|
||||
return $this->getCacheDriver()->has($token->toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param UnencryptedToken $token
|
||||
* @return bool
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function removeBlackList(UnencryptedToken $token): bool
|
||||
{
|
||||
return $this->getCacheDriver()->delete($token->toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $key
|
||||
* @param mixed|null $default
|
||||
* @return mixed
|
||||
*/
|
||||
public function getConfig(string $key, mixed $default = null): mixed
|
||||
{
|
||||
return Arr::get($this->config, $key, $default);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return JwtFacade
|
||||
*/
|
||||
private function getJwtFacade(): JwtFacade
|
||||
{
|
||||
return new JwtFacade(clock: $this->clock);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Signer
|
||||
*/
|
||||
private function getSigner(): Signer
|
||||
{
|
||||
return Arr::get($this->config, 'alg');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Key
|
||||
*/
|
||||
private function getSigningKey(): Key
|
||||
{
|
||||
return Arr::get($this->config, 'key');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return DriverInterface
|
||||
*/
|
||||
private function getCacheDriver(): DriverInterface
|
||||
{
|
||||
return $this->cacheManager->getDriver($this->getBlackConfig('connection'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @param mixed|null $default
|
||||
* @return mixed
|
||||
*/
|
||||
private function getBlackConfig(string $name, mixed $default = null): mixed
|
||||
{
|
||||
return Arr::get($this->config, 'blacklist.' . $name, $default);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Constraint
|
||||
*/
|
||||
private function getBlackListConstraint(): Constraint
|
||||
{
|
||||
return new BlackListConstraint((bool) $this->getBlackConfig('enable', false), $this->getCacheDriver());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \DateTimeImmutable $immutable
|
||||
* @return \DateTimeImmutable
|
||||
*/
|
||||
private function getExpireAt(\DateTimeImmutable $immutable): \DateTimeImmutable
|
||||
{
|
||||
return Carbon::create($immutable)
|
||||
->addSeconds(Arr::get($this->config, 'ttl', 3600))
|
||||
->toDateTimeImmutable();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \DateTimeImmutable $immutable
|
||||
* @return \DateTimeImmutable
|
||||
*/
|
||||
private function getRefreshExpireAt(\DateTimeImmutable $immutable): \DateTimeImmutable
|
||||
{
|
||||
return Carbon::create($immutable)
|
||||
->addSeconds(Arr::get($this->config, 'refresh_ttl', 7200))
|
||||
->toDateTimeImmutable();
|
||||
}
|
||||
}
|
||||
16
app/Lib/Jwt/AccessTokenConstraint.php
Normal file
16
app/Lib/Jwt/AccessTokenConstraint.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace App\Lib\Jwt;
|
||||
|
||||
use Lcobucci\JWT\Token;
|
||||
use Lcobucci\JWT\Validation\Constraint;
|
||||
use Lcobucci\JWT\Validation\ConstraintViolation;
|
||||
|
||||
class AccessTokenConstraint implements Constraint
|
||||
{
|
||||
|
||||
public function assert(Token $token): void
|
||||
{
|
||||
if (! $token->isRelatedTo('refresh')) throw ConstraintViolation::error('Token is not a refresh token', $this);
|
||||
}
|
||||
}
|
||||
33
app/Lib/Jwt/BlackListConstraint.php
Normal file
33
app/Lib/Jwt/BlackListConstraint.php
Normal file
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace App\Lib\Jwt;
|
||||
|
||||
use Hyperf\Cache\Driver\DriverInterface;
|
||||
use Lcobucci\JWT\Token;
|
||||
use Lcobucci\JWT\Validation\Constraint;
|
||||
use Lcobucci\JWT\Validation\ConstraintViolation;
|
||||
use Psr\SimpleCache\InvalidArgumentException;
|
||||
|
||||
class BlackListConstraint implements Constraint
|
||||
{
|
||||
/**
|
||||
* @param bool $enable
|
||||
* @param DriverInterface $cache
|
||||
*/
|
||||
public function __construct(
|
||||
private readonly bool $enable,
|
||||
private readonly DriverInterface $cache
|
||||
) {}
|
||||
|
||||
/**
|
||||
* @param Token $token
|
||||
* @return void
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function assert(Token $token): void
|
||||
{
|
||||
if ($this->enable !== true) return;
|
||||
|
||||
if ($this->cache->has($token->toString())) throw ConstraintViolation::error('Token is in blacklist', $this);
|
||||
}
|
||||
}
|
||||
15
app/Lib/Jwt/Clock.php
Normal file
15
app/Lib/Jwt/Clock.php
Normal file
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
namespace App\Lib\Jwt;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use DateTimeImmutable;
|
||||
use Psr\Clock\ClockInterface;
|
||||
|
||||
class Clock implements ClockInterface
|
||||
{
|
||||
public function now(): DateTimeImmutable
|
||||
{
|
||||
return Carbon::now()->toDateTimeImmutable();
|
||||
}
|
||||
}
|
||||
7
app/Lib/Jwt/Jwt.php
Normal file
7
app/Lib/Jwt/Jwt.php
Normal file
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
namespace App\Lib\Jwt;
|
||||
|
||||
use App\Interface\JwtInterface;
|
||||
|
||||
final class Jwt extends AbstractJwt implements JwtInterface {}
|
||||
51
app/Lib/Jwt/JwtFactory.php
Normal file
51
app/Lib/Jwt/JwtFactory.php
Normal file
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
namespace App\Lib\Jwt;
|
||||
|
||||
use App\Interface\JwtInterface;
|
||||
use Hyperf\Collection\Arr;
|
||||
use Hyperf\Contract\ConfigInterface;
|
||||
use function Hyperf\Support\make;
|
||||
|
||||
final class JwtFactory
|
||||
{
|
||||
public function __construct(
|
||||
private readonly ConfigInterface $config,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @return JwtInterface
|
||||
*/
|
||||
public function get(string $name = 'default'): JwtInterface
|
||||
{
|
||||
return make(Jwt::class, [
|
||||
'config' => $this->getConfig($name),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取场景配置
|
||||
* @param string $scene
|
||||
* @return array
|
||||
*/
|
||||
public function getConfig(string $scene): array
|
||||
{
|
||||
if ($scene === 'default') {
|
||||
return $this->config->get($this->getConfigKey());
|
||||
}
|
||||
return Arr::merge(
|
||||
$this->config->get($this->getConfigKey()),
|
||||
$this->config->get($this->getConfigKey($scene), [])
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @return string
|
||||
*/
|
||||
private function getConfigKey(string $name = 'default'): string
|
||||
{
|
||||
return 'jwt.' . $name;
|
||||
}
|
||||
}
|
||||
15
app/Lib/Jwt/RefreshTokenConstraint.php
Normal file
15
app/Lib/Jwt/RefreshTokenConstraint.php
Normal file
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
namespace App\Lib\Jwt;
|
||||
|
||||
use Lcobucci\JWT\Token;
|
||||
use Lcobucci\JWT\Validation\Constraint;
|
||||
use Lcobucci\JWT\Validation\ConstraintViolation;
|
||||
|
||||
class RefreshTokenConstraint implements Constraint
|
||||
{
|
||||
public function assert(Token $token): void
|
||||
{
|
||||
if ($token->isRelatedTo('refresh')) throw ConstraintViolation::error('Token is a refresh token', $this);
|
||||
}
|
||||
}
|
||||
16
app/Lib/Return/AdminReturn.php
Normal file
16
app/Lib/Return/AdminReturn.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace App\Lib\Return;
|
||||
|
||||
class AdminReturn extends CommonReturn
|
||||
{
|
||||
/**
|
||||
* 通用返回
|
||||
* @param array $res
|
||||
* @return array
|
||||
*/
|
||||
final protected function afterSuccess(array $res): array
|
||||
{
|
||||
return $res;
|
||||
}
|
||||
}
|
||||
16
app/Lib/Return/ApiReturn.php
Normal file
16
app/Lib/Return/ApiReturn.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace App\Lib\Return;
|
||||
|
||||
class ApiReturn extends CommonReturn
|
||||
{
|
||||
/**
|
||||
* 通用返回
|
||||
* @param array $res
|
||||
* @return array
|
||||
*/
|
||||
final protected function afterSuccess(array $res): array
|
||||
{
|
||||
return $res;
|
||||
}
|
||||
}
|
||||
53
app/Lib/Return/CommonReturn.php
Normal file
53
app/Lib/Return/CommonReturn.php
Normal file
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
namespace App\Lib\Return;
|
||||
|
||||
use App\Constants\ResultCode;
|
||||
|
||||
abstract class CommonReturn
|
||||
{
|
||||
/**
|
||||
* 通用 success 返回
|
||||
* @param string $msg
|
||||
* @param array $data
|
||||
* @param int|ResultCode $code
|
||||
* @param array $debug
|
||||
* @return array
|
||||
*/
|
||||
final public function success(string $msg = 'success', array $data = [], ResultCode|int $code = ResultCode::SUCCESS, array $debug = []): array
|
||||
{
|
||||
$res = [
|
||||
'code' => $code,
|
||||
'message' => $msg,
|
||||
'data' => $data
|
||||
];
|
||||
|
||||
return $this->afterSuccess(array_merge($res, $debug));
|
||||
}
|
||||
|
||||
/**
|
||||
* 通用 fail 返回
|
||||
* @param string $msg
|
||||
* @param array $data
|
||||
* @param int|ResultCode $code
|
||||
* @param array $debug
|
||||
* @return array
|
||||
*/
|
||||
final public function error(string $msg = 'failed', ResultCode|int $code = ResultCode::ERROR, array $data = [], array $debug = []): array
|
||||
{
|
||||
$res = [
|
||||
'code' => $code,
|
||||
'message' => $msg,
|
||||
'data' => $data
|
||||
];
|
||||
|
||||
return $this->afterSuccess(array_merge($res, $debug));
|
||||
}
|
||||
|
||||
/**
|
||||
* 通用类调子类返回方便切面类识别
|
||||
* @param array $res
|
||||
* @return array
|
||||
*/
|
||||
abstract protected function afterSuccess(array $res): array;
|
||||
}
|
||||
Reference in New Issue
Block a user