当前位置: 首页 > 知识库问答 >
问题:

Laravel JWT令牌在身份验证JWT方法中刷新后无效

蒯胜泫
2023-03-14

编辑:

阅读有关bug的讨论:https://github.com/tymondesigns/jwt-auth/issues/83

我原来的问题是:

我正在使用jwt auth my protected resources实现,该资源需要经过身份验证的用户,代码如下:

Route::group(['middleware' => ['before' => 'jwt.auth', 'after' => 'jwt.refresh']], function() {
    // Protected routes
});

当用户在API上登录时,将创建一个授权令牌,并将响应授权标头发送到调用资源的客户端应用程序。因此,客户端应用程序在截获任何响应的头上的授权令牌时,使用该令牌值设置变量/会话/任何内容,以便在下一次请求时再次发送到API。

“登录”后对受保护资源的第一个请求可以正常工作,但下一个客户端应用程序向API请求刷新令牌时会出现以下错误(API以json格式装载所有响应):

{
    "error": "token_invalid"
}

刷新令牌会发生什么?我的刷新令牌实现(设置为后中间件)是错误的?或者没有必要手动刷新客户端应用请求附带的所有授权令牌?

更新:

我按照这里的建议更新了jwt auth RefreshToken中间件,但是令牌\u无效仍然存在。

BUG:

我想我发现了发生了什么。请注意,在刷新方法中,旧令牌被添加到黑名单缓存启用情况:

// Tymon\JWTAuth\JWTManager
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']
        ])
    );
}

注意,在add to黑名单方法中,关键是旧令牌有效载荷中的jti参数:

// Tymon\JWTAuth\Blacklist
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;
}

因此,当hason黑名单方法被调用时,旧的令牌jti参数与new相同,因此新令牌在黑名单中:

// Tymon\JWTAuth\Blacklist
public function has(Payload $payload)
{
    return $this->storage->has($payload['jti']);
}

如果不需要黑名单功能,只需在jwt.php配置文件上设置为false即可。但我不能说它是否暴露了一些安全漏洞。

阅读有关bug的讨论:https://github.com/tymondesigns/jwt-auth/issues/83

共有1个答案

郦磊
2023-03-14

当我遇到这个问题时,我发现让我的项目工作的解决方案是在每个新请求上使用旧令牌的数据生成一个新令牌。

我的解决方案对我来说是糟糕的,丑陋的,如果你有很多异步请求,并且你的应用编程接口(或业务核心)服务器很慢,会产生更多的问题。

目前正在运行,但我将对此问题进行更多调查,因为在0.5.3版本之后,问题仍然存在。

例如:

请求1(GET /login):

Some guest data on token

请求2(POST /login答复):

User data merged with guest data on old token generating a new token

程序代码示例(您可以做得更好=),您可以在routes.php上运行这个,我说这是丑陋的哈哈:

// ----------------------------------------------------------------
// 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.
}

一些路线(保存用户移动设备的简单示例):

Route::group(['middleware' => ['before' => 'jwt.auth', 'after' => 'jwt.refresh']], function () use ($getLoggedUser, $mountAuthPayload) {
    Route::post('/session/device', function () use ($Response, $getLoggedUser, $mountAuthPayload) {
        $Response = new \Illuminate\Http\Response();
        $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;
    });
});
 类似资料:
  • 当用户发送他的用户名/密码时,服务会发送回JWT,该JWT存储在客户端的localStorage中。这个很管用。但是,我想使用刷新令牌继续向客户端发出新的JWT,使用户在使用该应用程序时保持登录状态。这些刷新令牌应该发在哪里?是否应在用户发送其用户名/密码时发出?如果是这样,那么在tymon/jwt-auth库中似乎没有一种方法可以将刷新令牌发送到客户机。请帮帮我,我很难想清楚这是怎么工作的。

  • 我正在做一个项目(没有生产级别,只是为了提高我的技能),我正在使用JWT来处理身份验证。从我所读到的内容来看,仅使用JWT作为访问令牌是非常不安全的,因此我们需要刷新令牌。因此,在登录时,服务器返回一个访问令牌和一个刷新令牌(我将存储在httpOnly cookie中)。访问令牌在短时间内到期,但刷新令牌在到期时用于获取新令牌。 我的问题是,我们何时使用刷新令牌来获取新的访问令牌?是当用户想要获得

  • 我有一个Rest Spring BootAPI,当用户验证API返回令牌jwt时,我在浏览器中注意到该令牌出现在响应头中 如何通过Reactjs将此令牌存储在本地存储浏览器中? 我的请求代码如下所示:

  • 我引用的是另一篇讨论在JWT中使用刷新令牌的SO帖子。 JWT(JSON Web令牌)自动延长到期时间 我有一个应用程序,它具有一个非常通用的体系结构,在这个体系结构中,我的客户机(web和移动)与一个REST API对话,然后再与一个服务层和数据层对话。 令牌每小时由客户端刷新一次。 如果用户令牌未被刷新(用户处于非活动状态且应用程序未打开)并且过期,则无论何时他们想要恢复,都需要登录。 我看到

  • 我在做一个全堆栈的web应用程序。我的前端由angular-cli组成,后端由node+Express构建。

  • 我在中间件中添加了以下代码,用于JWT Auth的用户身份验证,这对于中间件处理的所有路由都很有效。 但是对于一个使用Post方法的路由,令牌被正确传递,但我仍然得到: JWTException-无法从请求中分析令牌 在我尝试的同一路线上: 输出: 我能够看到我用来访问其他路由的有效令牌,并且该令牌在所有其他路由上都能正常工作。 提前感谢!!!