如何在注销时销毁JWT令牌?

时间:2016-06-22 06:15:01

标签: node.js jwt hapijs json-web-token

我在hapijs中使用jwt插件和策略。我能够在登录用户时创建jwt令牌,并通过'jwt'策略使用相同的令牌验证其他API。我在'request.state.USER_SESSION'中将令牌设置为一个cookie,其中USER_SESSION是令牌名称。另外,我没有将这些令牌保存在数据库中。但是如何在注销时销毁jwt令牌。请提出建议。

8 个答案:

答案 0 :(得分:42)

JWT存储在浏览器上,因此删除在客户端删除cookie的令牌

如果您还需要在服务器端到期之前使令牌无效,例如帐户已删除/阻止/暂停,密码已更改,权限已更改,用户已由管理员注销,请查看Invalidating JSON Web Tokens一些公共技术,如创建黑名单或旋转令牌

答案 1 :(得分:7)

创建令牌后,无法手动使令牌失效。因此,实际上无法像在会话中那样在服务器端使用JWT注销。

JWT是无状态的,这意味着您应该将所需的所有内容存储在有效负载中,并跳过对每个请求的数据库查询。但是,如果您计划具有严格的注销功能,即使您已从客户端清除了令牌,也无法等待令牌自动过期,那么您可能需要忽略无状态逻辑并执行一些查询。那么有什么解决方案?

  • 在令牌上设置合理的到期时间

  • 注销后从客户端删除存储的令牌

  • 在每个授权请求上针对黑名单查询提供的令牌

黑名单

所有不再有效且尚未过期的令牌的“黑名单”。您可以在文档上使用带有TTL选项的数据库,该数据库将设置为令牌过期之前剩余的时间。

Redis

Redis是黑名单的一个不错的选择,它将允许在内存中快速访问该列表。然后,在针对每个授权请求运行的某种中间件中,您应该检查提供的令牌是否在黑名单中。如果是这样,则应抛出未授权的错误。如果不是,则放手,JWT验证将处理它,并确定它是否已过期或仍处于活动状态。

答案 2 :(得分:1)

从客户端注销,最简单的方法是从浏览器的存储中删除令牌。

但是,如果您想销毁节点服务器上的令牌,该怎么办-

JWT软件包的问题在于它没有提供任何方法或方法来破坏令牌。

因此,为了销毁服务器端的令牌,您可以使用 jwt-redis软件包而不是JWT

此库(jwt-redis)完全重复了库jsonwebtoken的全部功能,但有一个重要的补充。 Jwt-redis允许您将令牌标签存储在redis中以验证有效性。 Redis中没有令牌标签会使令牌无效。要销毁jwt-redis中的令牌,有一种destroy方法

它以这种方式工作:

1)从npm安装jwt-redis

2)要创建-

var redis = require('redis');
var JWTR =  require('jwt-redis').default;
var redisClient = redis.createClient();
var jwtr = new JWTR(redisClient);

jwtr.sign(payload, secret)
    .then((token)=>{
            // your code
    })
    .catch((error)=>{
            // error handling
    });

3)要验证-

jwtr.verify(token, secret);

4)要摧毁-

jwtr.destroy(token)

注意:您可以像在JWT中提供的一样,在令牌登录期间提供expiresIn。

答案 3 :(得分:1)

您可以在令牌中添加“发出时间”,并为服务器上的每个用户维护“上次注销时间”。在检查令牌有效性时,还要在“上次注销时间”之后检查“签发时间”。

答案 4 :(得分:1)

如果您只想删除令牌,就像从前端应用程序中删除它一样简单,如果您清除存储令牌的 cookie

另一方面,如果您想让令牌无效,有几种方法可以做到,下面是一些方法

(1) 如果所有生成的令牌都存储在后端,那么清除存储就很简单,如果令牌已映射到用户,您只需清除特定用户的令牌.

(2) 您可以添加一个日期字段,例如“invalidate_before”以及 user,该字段应在更改密码时更新,退出所有设备等。 只需在此类事件上将 invalidate_before 更新为 currentTime()。 每次创建新令牌时,在令牌负载中添加创建时间, 要验证传入请求中的令牌,只需检查有效负载中的创建时间是否大于该用户在 db 中的 invalidate_before 时间

(3) 当你创建一个新用户时,只为那个用户创建一个秘密,然后你可以用那个特定的秘密签署每个用户令牌,就像 (2) 更改密码、从所有设备注销等事件,应该创建一个新的秘密。 这样你也可以通过检查令牌签名来使无效。

(2)(3) 的开销是,验证将是一个 2 步过程,它涉及数据库读取

答案 5 :(得分:0)

在控制器中创建注销功能,如下所示:

public function logout() {
    Auth::guard('api')->logout();

    return response()->json([
        'status' => 'success',
        'message' => 'logout'
    ], 200);
}

有关更多详细信息:https://dev-yakuza.github.io/en/laravel/jwt-logout/

答案 6 :(得分:0)

虽然其他答案为各种设置提供了详细的解决方案,但这可能会帮助正在寻找一般答案的人。

共有三个常规选项,选择一个或多个:

  1. 在客户端,使用javascript从浏览器中删除cookie。

  2. 在服务器端,将cookie值设置为空字符串或无用的东西(例如"deleted"),并将cookie过期时间设置为过去的时间。

  3. 在服务器端,更新存储在数据库中的刷新令牌。使用此选项可从所有已登录用户的设备中注销用户(其刷新令牌将无效,因此他们必须再次登录)。

答案 7 :(得分:0)

好的,所以我尝试了一些我想分享的东西,我认为这是一种非常简单有效的方法,因此基本上不是破坏您的令牌或黑名单,我们可以简单地在随机索引的中间添加一个随机值,甚至在它的结尾像一个随机数(或随机散列数),使任何人都更难逆转它并获得以前有效的令牌,这样做会使该令牌无效,因此用户不会从前面去任何地方-最后,您可以将用户重定向到再次登录(或者甚至从后端登录,但我更喜欢前端是否这样做),因此用户注销后,他们会被重定向到登录页面,一切都很好,这是我的代码。首先,我有一个 auth 中间件,如果令牌(密码和用户名)没问题,它会将令牌附加到 req.token 所以每当我调用这个中间件时,用户的令牌将被保存到 req.token

router.post('/logout', auth, async(req, res) => {
    try{
        let randomNumberToAppend = toString(Math.floor((Math.random() * 1000) + 1));
        let randomIndex = Math.floor((Math.random() * 10) + 1);
        let hashedRandomNumberToAppend = await bcrypt.hash(randomNumberToAppend, 10);
    
        // now just concat the hashed random number to the end of the token
        req.token = req.token + hashedRandomNumberToAppend;
        return res.status(200).json('logout');
    }catch(err){
        return res.status(500).json(err.message);
    }
});

现在它会将散列的随机数连接到令牌的末尾,这意味着它不再有效,因此用户必须再次登录,因为他们将被重定向到登录页面

相关问题