做一件事,无论大小,倘无恒心,是很不好的。而看一切太难,固然能使人无成,但若看得太容易,也能使事情无结果。
前言
在使用Hyperf的时候,想使用jwt权限认证,发现官方有推荐的几个完整auth包,也有单独封装的jwt组件,进去看了一下单独的jwt组件,发现他的文档使用方法并没有写的让人很明白,可能更多是想让大家使用hyperf-ext/auth这个组件。但是我们也可以基于hyperf-ext/jwt自己封装一下,以下为我摸索的使用方法。
安装
直接简单使用composer命令安装即可:
composer require hyperf-ext/jwt
配置
安装完成后,可以使用命令发布配置文件,文件位于config/autoload/jwt.php
php bin/hyperf.php vendor:publish hyperf-ext/jwt
具体的各种配置对应的会在它的文档里写清楚,这里不多做赘述。只说明一些用上的,比如
配置里面的密钥有两种方式,一种是随机字符串,一种是公钥私钥,两种方法都提供了对应的辅助命令帮你生成,二选一就好了,我这里选择了公私钥,所以执行一下命令
php bin/hyperf.php gen:jwt-keypair
然后按照命令台选择对应的选项(好像选择后要按回车确认),便会在.env文件上面生成对应的参数
然后便是jwt有效时间和刷新时间的配置,这个具体按照业务来,我为了方便调试,便设置了一些较短的时间
同理在.env文件中配置
JWT_TTL=20
JWT_REFRESH_TTL=120
然后需要配置一下redis
因为黑名单存储使用了缓存,当时我没有配置redis导致有问题,同时顺便安装一下composer require hyperf/cache
基本配置完成后,便可以开始使用了!
使用
官方文档里面的使用方法仅仅下面一段
<?php
declare(strict_types=1);
use HyperfExt\Jwt\Contracts\JwtFactoryInterface;
use HyperfExt\Jwt\Contracts\ManagerInterface;
class SomeClass
{
/**
* 提供了对 JWT 编解码、刷新和失活的能力。
*
* @var \HyperfExt\Jwt\Contracts\ManagerInterface
*/
protected $manager;
/**
* 提供了从请求解析 JWT 及对 JWT 进行一系列相关操作的能力。
*
* @var \HyperfExt\Jwt\Jwt
*/
protected $jwt;
public function __construct(
ManagerInterface $manager,
JwtFactoryInterface $jwtFactory
) {
$this->manager = $manager;
$this->jwt = $jwtFactory->make();
}
}
比较模糊也不知道具体怎么使用,于是去看了一下两个类,发现其实我们使用只需要用上\HyperfExt\Jwt\Jwt这个类就好了,因为里面已经封装了生成token的方法public function fromSubject(JwtSubjectInterface $subject): string,看一下
但这个方法需要传进一个实现
JwtSubjectInterface接口的对象,那就简单了,我们可以新建一个类实现这个接口便可以,这个接口需要实现两个方法
getJwtIdentifier()是为jwt里面的sub参数赋值,要求唯一,你可以按照你想要的方式生成一个唯一值就好了,我这里便用用户主键ID作为唯一值就好了
getJwtCustomClaims()这个方法是给payload添加我们自己想要补充的数据,它最后会在jwt默认的参数数组合并,生成最终的payload,了解完后,便开始写代码~
新建实现JwtSubjectInterface的类CustomerPayload.php
新建CustomerPayload.php,代码如下
<?php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://hyperf.wiki
* @contact group@hyperf.io
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
*/
namespace App\Helpers;
use Hyperf\Snowflake\IdGeneratorInterface;
use Hyperf\Utils\ApplicationContext;
use HyperfExt\Jwt\Contracts\JwtSubjectInterface;
class CustomerPayload implements JwtSubjectInterface
{
// 定义一个主键
protected string|int $primaryKey;
// 定义返回体
protected array $customerPayload;
public function __construct(string|int $primaryKey, array $customerPayload)
{
$this->primaryKey = $primaryKey;
$this->customerPayload = $customerPayload;
}
public function getJwtIdentifier()
{
// TODO: Implement getJwtIdentifier() method.
/*
* 如果你是有自己的算法生成唯一值,
* 可以不用像我这样传递一个值进来,
* 而是直接在这里写对应的方法返回就好
* 比如我使用hyperf生成雪花ID
* 如此上面的primaryKey便可删除
* 这些不是重点,你可以自己按照自己的方式来
*/
// $container = ApplicationContext::getContainer();
// $generator = $container->get(IdGeneratorInterface::class);
// return $generator->generate();
return $this->primaryKey;
}
public function getJwtCustomClaims(): array
{
// TODO: Implement getJwtCustomClaims() method.
return $this->customerPayload;
}
}
模拟登录获取token
写一个登录方法,模拟登录,然后生成对应的token返回
public function login(): \Psr\Http\Message\ResponseInterface
{
// 模拟登录后获取的用户信息
$userInfo = [
'user_id' => 1,
'username' => 'qiuyier',
];
// 获取一个JwtSubjectInterface对象
$payloadClass = new CustomerPayload(1, ['data' => $userInfo]);
// 获取一个jwt对象
$jwtInterface = make(JwtFactoryInterface::class);
$jwt = $jwtInterface->make();
// 调用fromSubject()方法获取token
$token = $jwt->fromSubject($payloadClass);
return $this->response->json([
'code' => 1,
'msg' => 'success',
'data' => [
'expire' => Carbon::now()->getTimestamp() + config('jwt_expire'),
'token' => $token,
],
]);
}
调用接口查看一下
由上图可以看到我们成功获取到了token
验证token信息
验证token信息同样只需要使用\HyperfExt\Jwt\Jwt这个类,便能简单验证token时效性,以及刷新token和获取token的内容,以下写法只是作为测试时方便使用,正常验证token应该是写在中间件里。
public function checkToken(): \Psr\Http\Message\ResponseInterface
{
try {
// 获取一个jwt对象
$jwtInterface = make(JwtFactoryInterface::class);
$jwt = $jwtInterface->make();
// 检查判断token时效性,如果有效则返回payload,无效则返回false
$payload = $jwt->check(true);
/*
* 这一部分为无感刷新token,
* 即通过在配置中的refresh_ttl的有效期内,
* 对token进行刷新,获得新的token
* 通过和前端约定的code判断是否更新旧token,
* 过了刷新时效则需要重新登录,
* 这部分自由选择,只是作为一个简单例子
*/
if (! $payload) {
/**
* 忽视过期情况获取旧的payload数组,
* 用于刷新token时保留原有的自定义数组.
*/
$payload = $jwt->getPayload(true);
/** setCustomClaims把原有的自定义数组传进去,
* 否则刷新token后则会丢失这一部分自定义数组,
* refresh为刷新token.
*/
$token = $jwt->setCustomClaims(['data' => $payload['data']])->refresh();
return $this->response->json([
'code' => 10086,
'msg' => 'token expire',
'data' => [
'expire' => Carbon::now()->getTimestamp() + config('jwt_expire'),
'token' => $token,
],
])->withStatus(401);
}
return $this->response->json([
'code' => 1,
'msg' => 'success',
'data' => $payload['data'],
]);
} catch (JwtException $e) {
// 抛出异常
return $this->response->json([
'code' => 0,
'msg' => $e->getMessage(),
'data' => null,
]);
}
}
调用一下接口,能成功获取我们自定义数组的内容,结果如图:
打印一下获取的payload,内容如下:
如果需要token过期自动刷新,则返回如下图:
这时如果我们再用旧token请求便会告知旧token被拉入黑名单了,如图:
如果超过刷新时长再去请求,则会告知token失效且不能再刷新,如图:
至此hyperf-ext/jwt基本使用已经介绍完毕,感谢阅读~