2014-10-10 45 views
0

出于安全原因,我们希望能够使所有用户的活动会话无效,例如,如果他们更改了密码,或者只是想强制注销他们的其他会议。我们使用Node.js,Express,express-sessions和Redis会话存储。在我们的应用程序,我们有(CoffeeScript的):使express.js中的所有单个用户的会话失效

app.use express.session 
    cookie: 
     maxAge: 5 * 24 * 60 * 60 * 1000 # 5 days in ms 
    store: new RedisStore(client: rclient) 
    key: "secret-key" 

Redis的商场工作通过映射唯一会话ID在会话中存储的任何数据。例如:

# In an HTTP request 
req.session.user = { _id: "user-id" } 
在Redis的

变为:

> get "sess:<session-id>" 
'{ "user": { "_id": "user-id" } } ' 

我们需要的是跟踪对应于每个用户ID的所有会话的方式,这样我们就可以从Redis的,如果我们想删除这些使用户的会话无效。以下注意事项适用于:

  1. 这些会话在Redis中给出的TTL等于 cookie的maxAge。每次会话的跟踪机制也应在此时间后过期 以避免陈旧的数据。
  2. 并非所有会话都必须与用户关联。有些仅用于跟踪匿名会话的详细信息。

当涉及到警告(1)时,在Redis中添加另一个反向查找键(例如,将user_id映射到用户的一组会话ID)失败。

这感觉像使用Express的其他网站必须遇到的问题,因为它是一种非常常见的安全模式。有没有人有任何建议如何跟踪用户会话,然后根据需要使其失效?

谢谢!

回答

3

我对类似的情况所做的是使用包含密钥名称中的用户标识的自定义会话ID。也许有一个更简单的方法,现在要做到这一点,但这里基本上是我必须做的设置自定义会话ID:

main.js:

var uid = require('uid'), 
    redis = require('redis'), 
    session = require('express-session'), 
    RedisStore = require('connect-redis')(session), 
    Session = session.Session, 
    Cookie = session.Cookie; 

var utils = require('./utils'); 

var COOKIE_SECRET = 'somethingrandom', 
    COOKIE_KEY = 'mycustomsession'; 

var redisClient = redis.createClient(), 
    redisStore = new RedisStore({ 
     client: redisClient, 
     ttl: 24 * 60 * 60, // 1 day session expiration 
     prefix: 'sess:' 
    }); 

// ... then inside the login route after user was successfully authenticated ... 
req.sessionStore = redisStore; 
req.sessionID = 'sess:' + user.id + ':' + uid(24); 
req.session = new Session(req); 
req.session.cookie = new Cookie({}); 
req.session.user = user; 
utils.createSession(req, res, COOKIE_KEY, COOKIE_SECRET); 

utils.js:

var onHeaders = require('on-headers'), 
    signature = require('cookie-signature'); 
exports.createSession = function(req, res, name, secret) { 
    var trustProxy = true; 
    // ripped from express-session 
    onHeaders(res, function() { 
    if (!req.session) 
     return; 

    var cookie = req.session.cookie 
     , proto = (req.headers['x-forwarded-proto'] || '').split(',')[0].toLowerCase().trim() 
     , tls = req.connection.encrypted || (trustProxy && 'https' == proto); 

    // only send secure cookies via https 
    if (cookie.secure && !tls) 
     return; 

    var val = 's:' + signature.sign(req.sessionID, secret); 
    res.cookie(name, val, cookie.data); 
    }); 

    // proxy end() to commit the session 
    var end = res.end; 
    res.end = function(data, encoding) { 
    res.end = end; 
    if (!req.session) return res.end(data, encoding); 
    req.session.resetMaxAge(); 
    req.session.save(function(err) { 
     if (err) console.error(err.stack); 
     res.end(data, encoding); 
    }); 
    }; 
}; 
+0

谢谢!那么你如何过期所有用户的会话?我们在Redis中存储了很多其他数据,并且使用'KEYS'或'SCAN'进行全面扫描并不是真的可行。我们需要能够做一些直接查询,我认为。 – 2014-10-13 15:47:18

+0

我使用SCAN模式,并没有任何问题。但是,您可以为会话和SCAN使用单独的数据库,或者使redis键不唯一(键入前缀+用户标识)以及包含该用户的JSON数组会话的值? – mscdex 2014-10-13 16:52:28

+0

谢谢,我认为在这种情况下,单独的数据库和'SCAN'的组合确实可以工作。感谢你的回答! – 2014-10-14 17:48:53