在使用身份验证JWT方法刷新它们之后,Laravel JWT令牌无效

May*_*onn 9 php authentication jwt laravel angularjs

编辑:

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

我的原始问题:

我使用jwt-auth实现我的受保护资源,需要经过身份验证的用户使用以下代码:

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

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

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

{
    "error": "token_invalid"
}
Run Code Online (Sandbox Code Playgroud)

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

更新:

我在这里建议更新jwt-auth RefreshToken中间件,但是token_invalid坚持.

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']
        ])
    );
}
Run Code Online (Sandbox Code Playgroud)

请注意,除了黑名单方法,密钥是来自旧令牌有效负载的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;
}
Run Code Online (Sandbox Code Playgroud)

因此,当调用黑名单方法时,旧标记jti param与new相同,因此新标记位于黑名单中:

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

如果您不需要在jwt.php配置文件中将黑名单功能设置为false.但我不能说它是否暴露于某些安全漏洞.

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

May*_*onn 4

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

我的解决方案对我有用,但是很糟糕,很丑陋,并且如果您有很多异步请求并且您的 API(或业务核心)服务器很慢,则可能会产生更多问题。

目前可以正常工作,但我会进一步调查这个问题,因为在 0.5.3 版本之后问题仍然存在。

例如:

请求 1(获取/登录):

Some guest data on token
Run Code Online (Sandbox Code Playgroud)

请求 2(POST/登录响应):

User data merged with guest data on old token generating a new token
Run Code Online (Sandbox Code Playgroud)

程序代码示例(你可以做得更好=)),你可以在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.
}
Run Code Online (Sandbox Code Playgroud)

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

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;
    });
});
Run Code Online (Sandbox Code Playgroud)