对于我正在从事的一个新的node.js项目,我正在考虑从基于cookie的会话方法(我的意思是,将id存储到用户浏览器中包含用户会话的键值存储中)切换到使用JSON Web Tokens (jwt)的基于令牌的会话方法(没有键值存储)。
这个项目是一个利用socket的游戏。IO——在一个会话(web和socket.io)中有多个通信通道的情况下,有一个基于令牌的会话会很有用。
如何使用jwt方法从服务器提供令牌/会话失效?
我还想了解使用这种范例应该注意哪些常见的(或不常见的)陷阱/攻击。例如,如果这种模式容易受到与基于会话存储/cookie的方法相同/不同类型的攻击。
所以,假设我有以下内容(改编自this和this):
会话存储登录:
app.get('/login', function(request, response) {
var user = {username: request.body.username, password: request.body.password };
// Validate somehow
validate(user, function(isValid, profile) {
// Create session token
var token= createSessionToken();
// Add to a key-value database
KeyValueStore.add({token: {userid: profile.id, expiresInMinutes: 60}});
// The client should save this session token in a cookie
response.json({sessionToken: token});
});
}
口令登录:
var jwt = require('jsonwebtoken');
app.get('/login', function(request, response) {
var user = {username: request.body.username, password: request.body.password };
// Validate somehow
validate(user, function(isValid, profile) {
var token = jwt.sign(profile, 'My Super Secret', {expiresInMinutes: 60});
response.json({token: token});
});
}
--
会话存储方法的注销(或失效)需要更新KeyValueStore
使用指定的令牌创建数据库。
在基于令牌的方法中似乎不存在这样的机制,因为令牌本身将包含通常存在于键值存储中的信息。
I ended up with access-refresh tokens, where refresh tokens uuids stored in database and access tokens uuids stored in cache server as a whitelist of valid access tokens. For example, I have critical changes in user data, for example, his access rights, next thing I do - I remove his access token from cache server whitelist and by the next access to any resource of my api, auth service will be asked for token's validity, then, if it isn't present in cache server whitelist, I will reject user's access token and force him to reauthorize by refresh token. If I want to drop user's session or all of his sessions, I simply drop all his tokens from whitelist and remove refresh tokens from database, so he musts re-enter credentials to continue accessing resources.
我知道,我的身份验证不再是无状态的,但公平地说,我为什么还要无状态的身份验证呢?
如果您希望能够撤销用户令牌,您可以跟踪DB上所有发出的令牌,并检查它们在类似会话的表上是否有效(存在)。
缺点是每次请求都要访问DB。
我还没有尝试过,但我建议使用以下方法来允许令牌撤销,同时将DB命中保持在最小值-
为了降低数据库检查率,根据某种确定性关联将所有已发行的JWT令牌分成X组(例如,按用户id的第一个数字分为10组)。
每个JWT令牌将保存组id和令牌创建时创建的时间戳。例如,{"group_id": 1, "timestamp": 1551861473716}
服务器将在内存中保存所有组id,每个组都有一个时间戳,该时间戳指示属于该组的用户的最后一次注销事件是什么时候。
例如,{"group1": 1551861473714, "group2": 1551861487293,…}
使用带有较旧组时间戳的JWT令牌的请求将被检查是否有效(DB hit),如果有效,将发出一个带有新时间戳的新JWT令牌供客户端将来使用。
如果令牌的组时间戳较新,则相信JWT (No DB hit)。
So -
We only validate a JWT token using the DB if the token has an old group timestamp, while future requests won't get validated until someone in the user's group will log-out.
We use groups to limit the number of timestamp changes (say there's a user logging in and out like there's no tomorrow - will only affect limited number of users instead of everyone)
We limit the number of groups to limit the amount of timestamps held in memory
Invalidating a token is a breeze - just remove it from the session table and generate a new timestamp for the user's group.
---------------- 这个答案一点迟到但可能会帮助别人 ----------------
从客户端,最简单的方法是从浏览器的存储中删除令牌。
但是,如果您想销毁节点服务器上的令牌-
JWT包的问题是它没有提供任何方法或方法来销毁令牌。
您可以使用上面提到的关于JWT的不同方法。但是这里我用的是jwt-redis。
所以为了在服务器端销毁令牌,你可以使用JWT -redis包而不是JWT
这个库(jwt-redis)完全重复了库jsonwebtoken的全部功能,只增加了一个重要的功能。Jwt-redis允许您将tokenIdentifier存储在redis中以验证有效性。redis中缺少tokenIdentifier使得令牌无效。要销毁jwt-redis中的令牌,有一个destroy方法
它是这样工作的:
从npm安装jwt-redis
创建:
Var redis = require('redis');
var JWTR = require('jwt-redis').default;
var redisClient = redis.createClient();
var jwtr = new jwtr (redisClient);
Const secret = 'secret';
const tokenIdentifier = 'test';
const payload = {jti: tokenIdentifier};//你也可以在payload中放入其他数据
jwtr。号(载荷、秘密)
不要犹豫((令牌)= > {
//你的代码
})
.catch((错误)= > {
//错误处理
});
验证:
jwtr。验证(令牌,秘密);
摧毁:
//如果jti在token的签名过程中传递,那么tokenIdentifier else token
jwtr.destroy(tokenIdentifier或token)
注意:
1).你可以在token的登录过程中提供expiresIn,就像JWT中提供的一样。
2).如果在token的签名过程中没有传递jti,那么jti将由库随机生成。
也许这能帮到你或其他人。谢谢。