mirror of
https://gitee.com/ctexthuang/hyperf_rbac_framework_server_ctexthuang.git
synced 2025-12-25 18:17:49 +08:00
feat : jwt
This commit is contained in:
@@ -19,4 +19,10 @@ class ResultCode extends AbstractConstants
|
||||
|
||||
#[Message("failed")]
|
||||
final public const int ERROR = 1;
|
||||
|
||||
#[Message("token已过期")]
|
||||
final public const int JWT_EXPIRED = 10001;
|
||||
|
||||
#[Message("token错误")]
|
||||
final public const int JWT_ERROR = 10002;
|
||||
}
|
||||
|
||||
32
app/Controller/Admin/AdminUserController.php
Normal file
32
app/Controller/Admin/AdminUserController.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Controller\Admin;
|
||||
|
||||
use App\Annotation\ResponseFormat;
|
||||
use App\Middleware\Token\AdminTokenMiddleware;
|
||||
use App\Service\Admin\AdminUser\UserService;
|
||||
use Hyperf\HttpServer\Annotation\Controller;
|
||||
use Hyperf\HttpServer\Annotation\Middleware;
|
||||
use Hyperf\HttpServer\Annotation\RequestMapping;
|
||||
use Hyperf\Validation\Annotation\Scene;
|
||||
|
||||
|
||||
#[Controller(prefix: "admin/adminUser")]
|
||||
#[ResponseFormat('admin')]
|
||||
#[Middleware(AdminTokenMiddleware::class)]
|
||||
class AdminUserController
|
||||
{
|
||||
#[RequestMapping(path: "getInfo", methods: "GET")]
|
||||
public function getInfo()
|
||||
{
|
||||
return (new UserService)->handle();
|
||||
}
|
||||
|
||||
#[RequestMapping(path: "refresh", methods: "POST")]
|
||||
public function refresh()
|
||||
{
|
||||
return (new UserService)->refresh();
|
||||
}
|
||||
}
|
||||
57
app/Exception/Handler/JwtExceptionHandler.php
Normal file
57
app/Exception/Handler/JwtExceptionHandler.php
Normal file
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
namespace App\Exception\Handler;
|
||||
|
||||
use App\Constants\ResultCode;
|
||||
use Psr\Container\ContainerExceptionInterface;
|
||||
use Psr\Container\NotFoundExceptionInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Throwable;
|
||||
use Lcobucci\JWT\Exception as JWTException;
|
||||
|
||||
class JwtExceptionHandler extends BaseErrExceptionHandler
|
||||
{
|
||||
/**
|
||||
* @param Throwable $throwable
|
||||
* @param ResponseInterface $response
|
||||
* @return ResponseInterface
|
||||
* @throws ContainerExceptionInterface
|
||||
* @throws NotFoundExceptionInterface
|
||||
*/
|
||||
public function handle(Throwable $throwable, ResponseInterface $response): ResponseInterface
|
||||
{
|
||||
if ($throwable instanceof JWTException) {
|
||||
$throwable = $this->modifyException($throwable);
|
||||
|
||||
// 传递给基类处理
|
||||
return $this->handlerResponse($throwable, $response);
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
protected function modifyException(Throwable $e): Throwable
|
||||
{
|
||||
// 根据不同的异常类型设置不同的code和message
|
||||
switch ($e->getMessage()) {
|
||||
case 'The token is expired':
|
||||
$e->code = ResultCode::JWT_EXPIRED;
|
||||
$e->message = 'token已过期';
|
||||
break;
|
||||
default:
|
||||
$e->code = ResultCode::JWT_ERROR;
|
||||
$e->message = 'token错误';
|
||||
}
|
||||
|
||||
return $e;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Throwable $throwable
|
||||
* @return bool
|
||||
*/
|
||||
public function isValid(Throwable $throwable): bool
|
||||
{
|
||||
return $throwable instanceof JWTException;
|
||||
}
|
||||
}
|
||||
10
app/Interface/CheckTokenInterface.php
Normal file
10
app/Interface/CheckTokenInterface.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace App\Interface;
|
||||
|
||||
use Lcobucci\JWT\UnencryptedToken;
|
||||
|
||||
interface CheckTokenInterface
|
||||
{
|
||||
public function checkJwt(UnencryptedToken $token): void;
|
||||
}
|
||||
@@ -11,6 +11,7 @@ use Lcobucci\JWT\Builder;
|
||||
use Lcobucci\JWT\JwtFacade;
|
||||
use Lcobucci\JWT\Signer;
|
||||
use Lcobucci\JWT\Signer\Key;
|
||||
use Lcobucci\JWT\Token\RegisteredClaims;
|
||||
use Lcobucci\JWT\UnencryptedToken;
|
||||
use Lcobucci\JWT\Validation\Constraint;
|
||||
use Lcobucci\JWT\Validation\Constraint\SignedWith;
|
||||
@@ -45,7 +46,8 @@ abstract class AbstractJwt implements JwtInterface
|
||||
$this->getSigner(),
|
||||
$this->getSigningKey(),
|
||||
function (Builder $builder, \DateTimeImmutable $immutable) use ($sub, $callable) {
|
||||
$builder = $builder->identifiedBy($sub);
|
||||
$builder = $builder->identifiedBy($sub)
|
||||
->issuedBy($this->getConfig('claims.'.RegisteredClaims::ISSUER,''));
|
||||
if ($callable !== null) {
|
||||
$builder = $callable($builder);
|
||||
}
|
||||
@@ -65,8 +67,10 @@ abstract class AbstractJwt implements JwtInterface
|
||||
$this->getSigner(),
|
||||
$this->getSigningKey(),
|
||||
function (Builder $builder, \DateTimeImmutable $immutable) use ($sub, $callable) {
|
||||
$builder = $builder->identifiedBy($sub);
|
||||
$builder = $builder->expiresAt($this->getRefreshExpireAt($immutable));
|
||||
$builder = $builder->identifiedBy($sub)
|
||||
->issuedBy($this->getConfig('claims.'.RegisteredClaims::ISSUER,''))
|
||||
->expiresAt($this->getRefreshExpireAt($immutable));
|
||||
|
||||
if ($callable !== null) {
|
||||
$builder = $callable($builder);
|
||||
}
|
||||
@@ -81,6 +85,7 @@ abstract class AbstractJwt implements JwtInterface
|
||||
*/
|
||||
public function parserAccessToken(string $accessToken): UnencryptedToken
|
||||
{
|
||||
echo 1;
|
||||
return $this->getJwtFacade()
|
||||
->parse(
|
||||
$accessToken,
|
||||
|
||||
14
app/Lib/Jwt/RequestScopedTokenTrait.php
Normal file
14
app/Lib/Jwt/RequestScopedTokenTrait.php
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace App\Lib\Jwt;
|
||||
|
||||
use Hyperf\Context\RequestContext;
|
||||
use Lcobucci\JWT\UnencryptedToken;
|
||||
|
||||
trait RequestScopedTokenTrait
|
||||
{
|
||||
public function getToken(): ?UnencryptedToken
|
||||
{
|
||||
return RequestContext::get()->getAttribute('token');
|
||||
}
|
||||
}
|
||||
80
app/Middleware/Token/AbstractTokenMiddleware.php
Normal file
80
app/Middleware/Token/AbstractTokenMiddleware.php
Normal file
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Middleware\Token;
|
||||
|
||||
use App\Interface\CheckTokenInterface;
|
||||
use App\Interface\JwtInterface;
|
||||
use App\Lib\Jwt\JwtFactory;
|
||||
use Hyperf\Collection\Arr;
|
||||
use Hyperf\Stringable\Str;
|
||||
use Lcobucci\JWT\Token;
|
||||
use Lcobucci\JWT\UnencryptedToken;
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Psr\Http\Server\RequestHandlerInterface;
|
||||
use Swow\Psr7\Message\ServerRequestPlusInterface;
|
||||
use function Hyperf\Support\value;
|
||||
|
||||
abstract class AbstractTokenMiddleware
|
||||
{
|
||||
public function __construct(
|
||||
protected ContainerInterface $container,
|
||||
protected readonly JwtFactory $jwtFactory,
|
||||
protected readonly CheckTokenInterface $checkToken,
|
||||
) {}
|
||||
|
||||
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
|
||||
{
|
||||
$token = $this->parserToken($request);
|
||||
$this->checkToken->checkJwt($token);
|
||||
$this->checkIssuer($token);
|
||||
|
||||
return $handler->handle(
|
||||
value(
|
||||
static function (ServerRequestPlusInterface $request, UnencryptedToken $token) {
|
||||
return $request->setAttribute('token', $token);
|
||||
},
|
||||
$request,
|
||||
$token
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param UnencryptedToken $token
|
||||
* @return void
|
||||
*/
|
||||
abstract public function checkIssuer(UnencryptedToken $token): void;
|
||||
|
||||
abstract public function getJwt(): JwtInterface;
|
||||
|
||||
/**
|
||||
* @param ServerRequestInterface $request
|
||||
* @return Token
|
||||
*/
|
||||
protected function parserToken(ServerRequestInterface $request): Token
|
||||
{
|
||||
return $this->getJwt()->parserAccessToken($this->getToken($request));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ServerRequestInterface $request
|
||||
* @return string
|
||||
*/
|
||||
protected function getToken(ServerRequestInterface $request): string
|
||||
{
|
||||
if ($request->hasHeader('Authorization')) {
|
||||
return Str::replace('Bearer ', '', $request->getHeaderLine('Authorization'));
|
||||
}
|
||||
if ($request->hasHeader('token')) {
|
||||
return $request->getHeaderLine('token');
|
||||
}
|
||||
if (Arr::has($request->getQueryParams(), 'token')) {
|
||||
return $request->getQueryParams()['token'];
|
||||
}
|
||||
return '';
|
||||
}
|
||||
}
|
||||
29
app/Middleware/Token/AdminTokenMiddleware.php
Normal file
29
app/Middleware/Token/AdminTokenMiddleware.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Middleware\Token;
|
||||
|
||||
use App\Exception\ErrException;
|
||||
use App\Interface\JwtInterface;
|
||||
use Lcobucci\JWT\Token\RegisteredClaims;
|
||||
use Lcobucci\JWT\UnencryptedToken;
|
||||
use function Hyperf\Support\env;
|
||||
|
||||
final class AdminTokenMiddleware extends AbstractTokenMiddleware
|
||||
{
|
||||
public function getJwt(): JwtInterface
|
||||
{
|
||||
return $this->jwtFactory->get('admin');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param UnencryptedToken $token
|
||||
* @return void
|
||||
*/
|
||||
public function checkIssuer(UnencryptedToken $token): void
|
||||
{
|
||||
$audience = $token->claims()->get(RegisteredClaims::ISSUER);
|
||||
if ($audience === env('APP_NAME') .'_admin') throw new ErrException('token错误');
|
||||
}
|
||||
}
|
||||
34
app/Service/Admin/AdminUser/UserService.php
Normal file
34
app/Service/Admin/AdminUser/UserService.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
/**
|
||||
* This service file is part of item.
|
||||
*
|
||||
* @author ctexthuang
|
||||
* @contact ctexthuang@qq.com
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Service\Admin\AdminUser;
|
||||
|
||||
use App\Lib\Jwt\RequestScopedTokenTrait;
|
||||
use App\Service\Admin\BaseAdminService;
|
||||
use Lcobucci\JWT\Token\RegisteredClaims;
|
||||
|
||||
class UserService extends BaseAdminService
|
||||
{
|
||||
use RequestScopedTokenTrait;
|
||||
|
||||
public function handle(): array
|
||||
{
|
||||
var_dump($this->getToken()->claims()->all());
|
||||
var_dump($this->getToken()->claims()->get(RegisteredClaims::ID));
|
||||
var_dump($this->getToken()->claims()->get(RegisteredClaims::AUDIENCE));
|
||||
|
||||
return $this->adminReturn->success();
|
||||
}
|
||||
|
||||
public function refresh(): array
|
||||
{
|
||||
return $this->adminReturn->success();
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,7 @@ use App\Interface\JwtInterface;
|
||||
use App\Lib\Jwt\JwtFactory;
|
||||
use App\Repository\AdminUserRepository;
|
||||
use App\Service\Admin\BaseAdminService;
|
||||
use App\Service\BaseTokenService;
|
||||
use Hyperf\Di\Annotation\Inject;
|
||||
|
||||
class LoginService extends BaseAdminService
|
||||
@@ -32,10 +33,10 @@ class LoginService extends BaseAdminService
|
||||
protected AdminUserRepository $userRepository;
|
||||
|
||||
/**
|
||||
* @var JwtFactory
|
||||
* @var BaseTokenService
|
||||
*/
|
||||
#[Inject]
|
||||
protected JwtFactory $jwtFactory;
|
||||
protected BaseTokenService $tokenService;
|
||||
|
||||
/**
|
||||
* @return array
|
||||
@@ -53,20 +54,12 @@ class LoginService extends BaseAdminService
|
||||
|
||||
if ($adminInfo->status == AdminUserStatusCode::DISABLE) throw new ErrException('用户已禁用');
|
||||
|
||||
$jwtHandle = $this->getJwt();
|
||||
$jwtHandle = $this->tokenService->getJwt();
|
||||
|
||||
return [
|
||||
return $this->adminReturn->success('success',[
|
||||
'access_token' => $jwtHandle->builderAccessToken((string) $adminInfo->id)->toString(),
|
||||
'refresh_token' => $jwtHandle->builderRefreshToken((string) $adminInfo->id)->toString(),
|
||||
'expire_at' => (int) $jwtHandle->getConfig('ttl', 0),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return JwtInterface
|
||||
*/
|
||||
private function getJwt(): JwtInterface
|
||||
{
|
||||
return $this->jwtFactory->get($this->jwt);
|
||||
]);
|
||||
}
|
||||
}
|
||||
54
app/Service/BaseTokenService.php
Normal file
54
app/Service/BaseTokenService.php
Normal file
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
/**
|
||||
* This service file is part of item.
|
||||
*
|
||||
* @author ctexthuang
|
||||
* @contact ctexthuang@qq.com
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Service;
|
||||
|
||||
use App\Exception\ErrException;
|
||||
use App\Interface\CheckTokenInterface;
|
||||
use App\Lib\Jwt\JwtFactory;
|
||||
use Hyperf\Di\Annotation\Inject;
|
||||
use Lcobucci\JWT\UnencryptedToken;
|
||||
use App\Interface\JwtInterface;
|
||||
|
||||
final class BaseTokenService implements CheckTokenInterface
|
||||
{
|
||||
private string $jwt = 'admin';
|
||||
|
||||
/**
|
||||
* @var JwtFactory
|
||||
*/
|
||||
#[Inject]
|
||||
protected JwtFactory $jwtFactory;
|
||||
|
||||
/**
|
||||
* @return JwtInterface
|
||||
*/
|
||||
public function getJwt(): JwtInterface
|
||||
{
|
||||
return $this->jwtFactory->get($this->jwt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return JwtInterface
|
||||
*/
|
||||
public function getApiJwt(): JwtInterface
|
||||
{
|
||||
return $this->jwtFactory->get('api');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param UnencryptedToken $token
|
||||
* @return void
|
||||
*/
|
||||
public function checkJwt(UnencryptedToken $token): void
|
||||
{
|
||||
$this->getJwt()->hasBlackList($token) && throw new ErrException('token已过期');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user