问题描述
阅读关于该错误的讨论:https://github.com/tymondesigns/jwt-auth/issues/83
Read the discussion about the bug at: https://github.com/tymondesigns/jwt-auth/issues/83
我的原始问题:
我正在使用 jwt-auth 我的受保护资源来实现,这些资源需要经过身份验证的用户代码:
I'm implement with jwt-auth my protected resources that require an authenticated user with bellow code:
Route::group(['middleware' => ['before' => 'jwt.auth', 'after' => 'jwt.refresh']], function() {
// Protected routes
});
当用户在 API 上登录"时,会创建一个授权令牌,并将响应授权标头发送到调用该资源的客户端应用程序.因此,客户端应用程序在拦截任何响应的标头上的授权令牌时,使用此令牌值设置变量/会话/任何内容,以便在下一次请求时再次发送到 API.
When user 'sign in' on API an Authorization token is created, and sent on response Authorization header to client application that call the resource. So, client applications when intercept a Authorization token on header of any response, set a variable/session/whatever with this token value, to send again to API on next request.
登录"后对受保护资源的第一次请求工作正常,但下一个客户端应用程序向 API 发出刷新令牌的请求,出现以下错误(API 以 json 格式装载所有响应):
The first request for a protected resource after 'login' works fine, but the next client application request to API with a refreshed token, gives the following error (API mount all responses in json format):
{
"error": "token_invalid"
}
刷新的令牌会发生什么?我的刷新令牌实现(设置为中间件后)是错误的?还是不需要手动刷新客户端应用请求附带的所有授权令牌?
What can be happen with refreshed tokens? My refresh token implementation (set as a after middleware) is wrong? Or isn't necessary to manually refresh all Authorization token that come with client apps requests?
更新:
我按照 here 的建议更新了 jwt-auth RefreshToken 中间件,但是token_invalid
持续存在.
I update the jwt-auth RefreshToken middleware as propose here, but the token_invalid
persist.
错误:
我想我发现了发生了什么.请注意,在刷新方法中,旧令牌被添加到黑名单缓存中启用:
I guess that I found what happens. Note that in the refresh method, old token is added to blacklist cache case enabled:
// TymonJWTAuthJWTManager
public function refresh(Token $token)
{
$payload = $this->decode($token);
if ($this->blacklistEnabled) {
// invalidate old token
$this->blacklist->add($payload);
}
// return the new token
return $this->encode(
$this->payloadFactory->setRefreshFlow()->make([
'sub' => $payload['sub'],
'iat' => $payload['iat']
])
);
}
请注意,除了黑名单方法,关键是旧令牌有效负载中的 jti 参数:
And note that in add to blacklist method the key is the jti param from old token payload:
// TymonJWTAuthBlacklist
public function add(Payload $payload)
{
$exp = Utils::timestamp($payload['exp']);
// there is no need to add the token to the blacklist
// if the token has already expired
if ($exp->isPast()) {
return false;
}
// add a minute to abate potential overlap
$minutes = $exp->diffInMinutes(Utils::now()->subMinute());
$this->storage->add($payload['jti'], [], $minutes);
return true;
}
因此,当调用has on blacklist方法时,旧token jti参数与new相同,所以新token在黑名单中:
Thus, when has on blacklist method is called, the old token jti param is the same that the new, so the new token is in blacklist:
// TymonJWTAuthBlacklist
public function has(Payload $payload)
{
return $this->storage->has($payload['jti']);
}
如果您不需要黑名单功能,只需在 jwt.php 配置文件中设置为 false.但我不能说它是否暴露了一些安全漏洞.
If you don't need the blacklist functionality just set to false on jwt.php configuration file. But I can't say if it expose to some security vulnerability.
阅读关于该错误的讨论:https://github.com/tymondesigns/jwt-auth/issues/83
Read the discussion about the bug at: https://github.com/tymondesigns/jwt-auth/issues/83
推荐答案
当我遇到这个问题时,我发现让我的项目工作的解决方案是在每个新请求上使用旧令牌中的数据生成一个新令牌.
When I get this issue, the solution that I found to get my project working was to generate a new token with data from older token on each new request.
适用于我的解决方案很糟糕,很丑陋,并且如果您有很多异步请求并且您的 API(或业务核心)服务器很慢,则会产生更多问题.
My solution, that works for me, is bad, ugly, and can generate more issues if you have many async requests and your API(or business core) server is slow.
目前正在工作,但我会进一步调查这个问题,因为在 0.5.3 版本之后问题仍然存在.
For now is working, but I will investigate more this issue, cause after 0.5.3 version the issue continues.
例如:
请求 1 (GET/login):
Request 1 (GET /login):
Some guest data on token
请求 2(POST/登录响应):
Request 2 (POST /login response):
User data merged with guest data on old token generating a new token
程序代码示例(你可以做得更好=)),你可以在routes.php上运行这个,我说这很难看哈哈:
Procedural code example(you can do better =) ), you can run this on routes.php out of routes, I say that is ugly haha:
// ----------------------------------------------------------------
// AUTH TOKEN WORK
// ----------------------------------------------------------------
$authToken = null;
$getAuthToken = function() use ($authToken, $Response) {
if($authToken === null) {
$authToken = JWTAuth::parseToken();
}
return $authToken;
};
$getLoggedUser = function() use ($getAuthToken) {
return $getAuthToken()->authenticate();
};
$getAuthPayload = function() use ($getAuthToken) {
try {
return $getAuthToken()->getPayload();
} catch (Exception $e) {
return [];
}
};
$mountAuthPayload = function($customPayload) use ($getLoggedUser, $getAuthPayload) {
$currentPayload = [];
try {
$currentAuthPayload = $getAuthPayload();
if(count($currentAuthPayload)) {
$currentPayload = $currentAuthPayload->toArray();
}
try {
if($user = $getLoggedUser()) {
$currentPayload['user'] = $user;
}
$currentPayload['isGuest'] = false;
} catch (Exception $e) {
// is guest
}
} catch(Exception $e) {
// Impossible to parse token
}
foreach ($customPayload as $key => $value) {
$currentPayload[$key] = $value;
}
return $currentPayload;
};
// ----------------------------------------------------------------
// AUTH TOKEN PAYLOAD
// ----------------------------------------------------------------
try {
$getLoggedUser();
$payload = ['isGuest' => false];
} catch (Exception $e) {
$payload = ['isGuest' => true];
}
try {
$payload = $mountAuthPayload($payload);
} catch (Exception $e) {
// Make nothing cause token is invalid, expired, etc., or not exists.
// Like a guest session. Create a token without user data.
}
一些路线(保存用户移动设备的简单示例):
Some route(simple example to save user mobile device):
Route::group(['middleware' => ['before' => 'jwt.auth', 'after' => 'jwt.refresh']], function () use ($getLoggedUser, $mountAuthPayload) {
Route::post('/session/device', function () use ($Response, $getLoggedUser, $mountAuthPayload) {
$Response = new IlluminateHttpResponse();
$user = $getLoggedUser();
// code to save on database the user device from current "session"...
$payload = app('tymon.jwt.payload.factory')->make($mountAuthPayload(['device' => $user->device->last()->toArray()]));
$token = JWTAuth::encode($payload);
$Response->header('Authorization', 'Bearer ' . $token);
$responseContent = ['setted' => 'true'];
$Response->setContent($responseContent);
return $Response;
});
});
这篇关于Laravel JWT 令牌在身份验证 JWT 方法中刷新后无效的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!