为保证和前端交互过程中,用户可以自动刷新token
1.创建一个中间件文件,命名为 RefreshToken
<?php
namespace App\Http\Middleware;
use Auth;
use Closure;
use Tymon\JWTAuth\JWTAuth;
use Tymon\JWTAuth\Exceptions\JWTException;
use Tymon\JWTAuth\Http\Middleware\BaseMiddleware;
use Tymon\JWTAuth\Exceptions\TokenExpiredException;
use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException;
class RefreshToken extends BaseMiddleware
{
function handle($request, Closure $next)
{
// 检查此次请求中是否带有 token,如果没有则抛出异常。
$this->checkForToken($request);
// 使用 try 包裹,以捕捉 token 过期所抛出的 TokenExpiredException 异常
try {
// 检测用户的登录状态,如果正常则通过
if ($this->auth->parseToken()->authenticate()) {
return $next($request);
}
throw new UnauthorizedHttpException('jwt-auth', '未登录');
} catch (TokenExpiredException $exception) {
// 此处捕获到了 token 过期所抛出的 TokenExpiredException 异常,我们在这里需要做的是刷新该用户的 token 并将它添加到响应头中
try {
/*
* token在刷新期内,是可以自动执行刷新获取新的token的
* 当JWT_BLACKLIST_ENABLED=false时,可以在JWT_REFRESH_TTL时间内,无限次刷新使用旧的token换取新的token
* 当JWT_BLACKLIST_ENABLED=true时,刷新token后旧的token即刻失效,被放入黑名单
* */
// 刷新用户的 token
$token = $this->auth->refresh();
// 使用一次性登录以保证此次请求的成功
Auth::guard('api')->onceUsingId($this->auth->manager()->getPayloadFactory()->buildClaimsCollection()->toPlainArray()['sub']);
} catch (JWTException $exception) {
// 如果捕获到此异常,即代表 refresh 也过期了,用户无法刷新令牌,需要重新登录。
throw new UnauthorizedHttpException('jwt-auth', $exception->getMessage());
}
}
// 在响应头中返回新的 token
return $this->setAuthenticationHeader($next($request), $token);
}
}
2.修改App\Http\Kernel.pho文件
protected $routeMiddleware = [
//......
'token.refresh' => \App\Http\Middleware\RefreshToken::class,
//......
];
3.修改routes.api.php文件
// 需要 token 验证的接口
$api->group(['middleware' => ['token.refresh','auth.jwt']], function($api) {
//.......
});
4.修改.env文件
#Jwt
JWT_SECRET=HSKxIUfwCdJj5gadbqfQo5im9zje95g9
#token有效时间,单位:分钟, 有效时间调整为2个小时
JWT_TTL=120
#为了使令牌无效,您必须启用黑名单。如果不想或不需要此功能,请将其设置为 false。
#当JWT_BLACKLIST_ENABLED=false时,可以在JWT_REFRESH_TTL时间内,无限次刷新使用旧的token换取新的token
#当JWT_BLACKLIST_ENABLED=true时,刷新token后旧的token即刻失效,被放入黑名单
JWT_BLACKLIST_ENABLED=true
#当多个并发请求使用相同的JWT进行时,由于 access_token 的刷新 ,其中一些可能会失败,以秒为单位设置请求时间以防止并发的请求失败。
#时间为10分钟,10分钟之内可以拿旧的token换取新的token。当JWT_BLACKLIST_ENABLED为true时,可以保证不会立即让token失效
JWT_BLACKLIST_GRACE_PERIOD=600
5.备注:
'ttl' => env('JWT_TTL', 60), //单位分钟
b.刷新时间,刷新时间指的是在这个时间内可以凭旧 token 换取一个新 token。例如 token 有效时间为 60 分钟,刷新时间为 20160 分钟,在 60 分钟内可以通过这个 token 获取新 token,但是超过 60 分钟是不可以的,然后你可以一直循环获取,直到总时间超过 20160 分钟,不能再获取。 这里要强调的是,是否在刷新期可以一直用旧的token获取新的token,这个是由blacklist_enabled这个配置决定的,这个是指是否开启黑名单,默认是开启的,即刷新后,旧token立马加入黑名单,不可在用。
'refresh_ttl' => env('JWT_REFRESH_TTL', 20160),
c.宽限时间,宽限时间是为了解决并发请求的问题,假如宽限时间为 0s ,那么在新旧 token 交接的时候,并发请求就会出错,所以需要设定一个宽限时间,在宽限时间内,旧 token 仍然能够正常使用
// 宽限时间需要开启黑名单(默认是开启的),黑名单保证过期token不可再用
'blacklist_enabled' => env('JWT_BLACKLIST_ENABLED', true)
// 设定宽限时间,单位:秒
'blacklist_grace_period' => env('JWT_BLACKLIST_GRACE_PERIOD', 600)