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

kpbpu008  于 2023-04-11  发布在  Node.js
关注(0)|答案(8)|浏览(374)

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

b09cbbtk

b09cbbtk1#

JWT存储在浏览器上,因此删除客户端的cookie
如果您还需要在令牌到期之前从服务器端使其失效,例如帐户被删除/阻止/暂停,密码更改,权限更改,用户被管理员注销,请查看Invalidating JSON Web Tokens了解一些常见技术,如创建黑名单或轮换令牌。

bzzcjhmw

bzzcjhmw2#

在创建令牌后,您无法手动使其过期。因此,您无法在服务器端使用JWT注销,就像使用会话一样。
JWT是无状态的,这意味着你应该把你需要的所有东西都存储在payload中,而不需要对每个请求执行DB查询。但是如果你计划有一个严格的注销功能,即使你已经从客户端清理了令牌,也不能等待令牌自动过期,那么你可能需要忽略无状态逻辑并执行一些查询。那么有什么解决方案呢?

  • 为令牌设置合理的过期时间
  • 注销时从客户端删除存储的令牌
  • 针对每个授权请求,根据黑名单查询提供的令牌

黑名单

所有不再有效且尚未过期的令牌的“黑名单”。您可以使用具有TTL选项的DB,该选项将被设置为令牌过期前的剩余时间。

Redis

Redis是blacklist的一个不错的选择,它可以快速访问内存中的列表。然后,在运行在每个授权请求上的某种中间件中,您应该检查所提供的令牌是否在Blacklist中。如果是,则应该抛出未授权错误。如果不是,则让它离开,JWT验证将处理它并确定它是否过期或仍然有效。
有关更多信息,请参阅How to log out when using JWT。Arpy Vanyan(credit and reference)

aij0ehis

aij0ehis3#

从客户端注销时,最简单的方法是从浏览器的存储中删除令牌。
但是,如果你想销毁Node服务器上的token怎么办-

JWT包的问题是它没有提供任何方法或途径来销毁令牌。
因此,为了在服务器端销毁令牌,您可以使用jwt-redis包而不是JWT

本库(jwt-redis)完全重复了jsonwebtoken库的全部功能,但有一个重要的补充。Jwt-redis允许您将token标签存储在redis中以验证有效性。redis中没有token标签会导致token无效。要销毁jwt-redis中的token,有一个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)

注意:您可以在登录token时提供expiresIn,与JWT提供的一样。

n6lpvg4x

n6lpvg4x4#

如果您只想删除令牌,则只需将其从前端应用程序中删除即可。在这种情况下,请清除存储令牌的cookie
另一方面,如果你想使令牌无效,有几种方法可以做到这一点,下面是一些方法

(1)如果所有生成的token都存储在后端,那么就像清除存储一样简单,如果token已经Map到用户,那么您可以只清除特定用户的token。
(2)您可以在user中添加类似
invalidate_before
这样的日期字段,在更改密码、从所有设备注销等情况下更新该日期字段。只需将invalidate_before更新为currentTime即可()。每次创建新令牌时,将创建时间添加到令牌有效负载中,要验证传入请求上的令牌,只需检查有效负载中的创建时间是否大于数据库中该用户的invalidate_before时间
**(3)当您创建一个新用户时,只为该用户创建一个密钥,然后您可以使用该特定密钥对每个用户令牌进行签名,就像在(2)**事件中一样,如更改密码,从所有设备注销等,应该创建一个新的密钥。这样您也可以通过检查令牌签名来使其无效。
**(2)(3)**的开销是,验证将是一个两步过程,并且它涉及数据库阅读

编辑:对于**(3),您可以使用盐来代替(最终的秘密将是公共秘密+特定用户的盐**),因此您可以通过更改盐来使单个用户的令牌无效,或者通过更改公共秘密来使所有用户的令牌无效

rxztt3cl

rxztt3cl5#

您可以为令牌添加“发行时间”,并为服务器上的每个用户维护“最后注销时间”。当您检查令牌有效性时,还可以检查“发行时间”是否在“最后注销时间”之后。

krugob8w

krugob8w6#

虽然其他答案为各种设置提供了详细的解决方案,但这可能会帮助那些只在寻找一般答案的人。
有三个常规选项,选择一个或多个:
1.在客户端,使用javascript从浏览器中删除cookie。
1.在服务器端,将cookie值设置为空字符串或一些无用的值(例如"deleted"),并将cookie过期时间设置为过去的时间。
1.在服务器端,更新存储在数据库中的refreshtoken。使用此选项可以从用户登录的所有设备注销用户(他们的refreshtoken将变为无效,他们必须重新登录)。

hsvhsicv

hsvhsicv7#

如果你使用的是passportJs的jwt策略,你可以通过改变secretOrKey来使tokes失效。它会使所有用户的tokes失效。
我很确定你正在使用的auth库有一些类似的属性。

xyhw6mcr

xyhw6mcr8#

好了,我尝试了一些我想分享的东西,我认为这是一个非常简单和有效的方法,所以基本上不是破坏你的令牌或黑名单,我们可以简单地在中间添加一个随机值,在一个随机索引中,甚至在它的末尾,就像一个随机数(或随机散列数)以使任何人更难反转它并获得先前有效的令牌,这样做会使此令牌无效,因此用户将无法访问任何地方,并且您可以从前端重定向用户以再次登录(或者甚至从后端,但我更喜欢如果前端这样做),所以用户注销,他们被重定向到登录页面,这一切都很好,这是我的代码。首先,我有一个认证中间件,如果令牌(密码和用户名)是好的,它附加令牌到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);
    }
});

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

相关问题