总结
- 安装
hyperf-ext/auth
,hyperf-ext/jwt
并配置好 - 修改
auth
的配置文件-
guard
默认改为api
-
api
的驱动使用JwtGuard
,提供者provider
的模型改为自己的user
用户模型
-
- 修改
user
用户模型,实现俩个接口JwtSubjectInterface
AuthenticatableInterface
- 使用
use HyperfExt\Auth\Authenticatable;
实现AuthenticatableInterface
- 添加
getJwtIdentifier()
getJwtCustomClaims()
实现JwtSubjectInterface
。getJwtIdentifier()
方法要返回主键ID
- 使用
- 创建中间件
- 使用
JwtFactory
类的make()
,获得$jwt
对象 - 使用
$jwt->checkOrFail()
验证Token
,失败返回response
- 使用
- 使用
$auth = make(AuthManagerInterface::class)->guard('api');
$auth->login(UserModel);
$auth->user();
安装一些组件
经评论提醒 加上
hyperf-ext/hashing
此组件为hyperf-ext/auth
依赖
composer require hyperf-ext/auth
composer require hyperf-ext/hashing
composer require hyperf-ext/jwt
发布配置
php bin/hyperf.php vendor:publish hyperf-ext/auth
php bin/hyperf.php vendor:publish hyperf-ext/hashing
php bin/hyperf.php vendor:publish hyperf-ext/jwt
# 生成 jwt 密钥
php bin/hyperf.php gen:jwt-secret
配置 auth
修改默认guard
(可不改,后面使用时需指定),将api
使用的provider
的模型改为你的用户模型
'default' => [
'guard' => 'api',
]
'providers' => [
'users' => [
'driver' => \HyperfExt\Auth\UserProviders\ModelUserProvider::class,
'options' => [
'model' => App\Model\User::class, # 用户模型
'hash_driver' => 'bcrypt',
],
],
]
config/autoload/auth.php
添加auth助手函数
新建一个文件写入如下代码,方便使用
if (!function_exists('auth')) {
/**
* Auth认证辅助方法
* @param string|null $guard
* @return \HyperfExt\Auth\Contracts\GuardInterface|\HyperfExt\Auth\Contracts\StatefulGuardInterface|\HyperfExt\Auth\Contracts\StatelessGuardInterface
*/
function auth(string $guard = null)
{
if (is_null($guard)) $guard = config('auth.default.guard');
return make(AuthManagerInterface::class)->guard($guard);
}
}
app/Functions.php
将上面文件加入到 composer
的自动加载中
"autoload": {
"files": [
"app/Functions.php"
]
}
composer.json
修改user模型
实现 jwt,auth 的接口
class User extends Model implements JwtSubjectInterface,AuthenticatableInterface
增加 auth 支持
use Authenticatable;
增加 jwt接口需要的方法
public function getJwtCustomClaims(): array
{
return [
'guard' => 'api' // 添加自定义信息
];
}
public function getJwtIdentifier()
{
return $this->getKey();
}
新建一个 ApiResponse Trait
方便处理接口返回内容,或者可以自己手动返回
ResponseInterface
withBody(Stream)
<?php
declare(strict_types=1);
namespace App\Traits;
use Hyperf\HttpMessage\Stream\SwooleStream;
use Hyperf\Utils\Codec\Json;
use Hyperf\Utils\Context;
use Hyperf\Utils\Contracts\Arrayable;
use Hyperf\Utils\Contracts\Jsonable;
use Psr\Http\Message\ResponseInterface;
/**
* Trait ApiResponse
* @author Colorado
* @package App\Traits
*/
trait ApiResponse
{
private $httpCode = 200;
private $errorCode = 100000;
private $errorMsg = '系统错误';
private $headers = [];
protected $response;
/**
* 成功响应
* @param mixed $data
* @return ResponseInterface
*/
public function success($data): ResponseInterface
{
return $this->respond($data);
}
/**
* 错误返回
* @param int $err_code 错误业务码
* @param string $err_msg 错误信息
* @param array $data 额外返回的数据
* @return ResponseInterface
*/
public function error(int $err_code = null,string $err_msg = null, array $data = []): ResponseInterface
{
return $this->setHttpCode($this->httpCode == 200 ? 400 : $this->httpCode)
->respond([
'err_code' => $err_code ?? $this->errorCode,
'err_msg' => $err_msg ?? $this->errorMsg,
'data' => $data
]);
}
/**
* 设置http返回码
* @param int $code http返回码
* @return $this
*/
final public function setHttpCode(int $code = 200): self
{
$this->httpCode = $code;
return $this;
}
/**
* 设置返回头部header值
* @param string $key
* @param $value
* @return $this
*/
public function addHttpHeader(string $key, $value): self
{
$this->headers += [$key => $value];
return $this;
}
/**
* 批量设置头部返回
* @param array $headers header数组:[key1 => value1, key2 => value2]
* @return $this
*/
public function addHttpHeaders(array $headers = []): self
{
$this->headers += $headers;
return $this;
}
/**
* @param null|array|Arrayable|Jsonable|string $response
* @return ResponseInterface
*/
private function respond($response): ResponseInterface
{
if (is_string($response)) {
return $this->response()->withAddedHeader('content-type', 'text/plain')->withBody(new SwooleStream($response));
}
if (is_array($response) || $response instanceof Arrayable) {
return $this->response()
->withAddedHeader('content-type', 'application/json')
->withBody(new SwooleStream(Json::encode($response)));
}
if ($response instanceof Jsonable) {
return $this->response()
->withAddedHeader('content-type', 'application/json')
->withBody(new SwooleStream((string)$response));
}
return $this->response()->withAddedHeader('content-type', 'text/plain')->withBody(new SwooleStream((string)$response));
}
/**
* 获取 Response 对象
* @return mixed|ResponseInterface|null
*/
protected function response(): ResponseInterface
{
$response = Context::get(ResponseInterface::class);
foreach ($this->headers as $key => $value) {
$response = $response->withHeader($key, $value);
}
return $response;
}
}
创建验证中间件
hyperf
的中间件和laravel
的中间件实现方式不同,需要创建新的中间件
验证失败时,返回错误 response
php bin/hyperf.php gen:middleware JwtMiddleware
<?php
declare(strict_types=1);
namespace App\Middleware;
use App\Traits\ApiResponse;
use Hyperf\Di\Annotation\Inject;
use HyperfExt\Jwt\Exceptions\TokenExpiredException;
use HyperfExt\Jwt\JwtFactory;
use Psr\Container\ContainerInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
class JwtMiddleware implements MiddlewareInterface
{
use ApiResponse;
/**
* @var ContainerInterface
*/
protected $container;
/**
* 注入 JwtFactory
* @Inject
* @var JwtFactory
*/
private $jwtFactory;
public function __construct(ContainerInterface $container)
{
$this->container = $container;
}
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
$jwt = $this->jwtFactory->make();
try {
$jwt->checkOrFail();
} catch (\Exception $exception) {
if (! $exception instanceof TokenExpiredException) {
return $this->error(40000,$exception->getMessage());
}
// 尝试自动刷新 token
try {
$token = $jwt->getToken();
// 刷新token
$new_token = $jwt->getManager()->refresh($token);
// 解析token载荷信息
$payload = $jwt->getManager()->decode($token, false, true);
// 旧token加入黑名单
$jwt->getManager()->getBlacklist()->add($payload);
// 一次性登录,保证此次请求畅通
auth($payload->get('guard') ?? config('auth.default.guard'))
->onceUsingId($payload->get('sub'));
return $handler->handle($request)->withHeader('authorization', 'bearer ' . $new_token);
} catch (\Exception $exception) {
// Token 验证失败
return $this->setHttpCode(401)->error(40001,$exception->getMessage());
}
}
return $handler->handle($request);
}
}
使用中间件
配置路由 或者 注解
OPTIONS = ['middleware' => [ JwtMiddleware::class ]];
Router::get(PATH,FALLBACK,OPTIONS);
Router::addGroup(PREFIX,FALLBACK,OPTIONS);
Router::addRoute(FUNCTION,PATH,FALLBACK,OPTIONS);
单个
/**
* @Middleware(FooMiddleware::class)
*/
多个
/**
* @Middlewares({
* @Middleware(FooMiddleware::class),
* @Middleware(BarMiddleware::class)
* })
*/
在控制器中使用 auth
$token = auth()->login($User::first());
$user = auth()->user();
$userid = auth()->id();
转载请注明简书或博客地址 https://saopanda.top/php/16684d5d.html