【问题标题】:Invalidating all of a single user's sessions in express.js使 express.js 中单个用户的所有会话无效
【发布时间】:2014-12-05 20:12:56
【问题描述】:

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

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 等于 曲奇饼。每个会话的跟踪机制也应该过期 在此时间之后,以避免过时的数据。
  2. 并非所有会话都必须与用户相关联。有些只是用于跟踪匿名会话详细信息。

在 Redis 中添加另一个反向查找键(例如将 user_id 映射到用户的一组会话 id)的简单方法在遇到警告 (1) 时失败了。

这感觉像是其他使用 Express 的网站一定遇到过的问题,因为这是一种非常常见的安全模式。是否有人对如何跟踪用户会话然后按需使其无效有任何建议?

谢谢!

【问题讨论】:

    标签: node.js session express


    【解决方案1】:

    我针对类似情况所做的是使用自定义会话 ID,该会话 ID 在密钥名称中包含用户 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);
        });
      };
    };
    

    【讨论】:

    • 谢谢!然后如何使用户的所有会话过期?我们在 Redis 中存储了许多其他数据,使用 KEYSSCAN 进行全面扫描实际上并不可行。我认为我们需要能够进行某种直接查找。
    • 我将SCAN 与模式一起使用,没有任何问题。但是,您可以只为会话和SCAN 使用单独的数据库,或者使 redis 键不唯一(以前缀 + 用户 ID 为键)和包含该用户会话的 JSON 数组的值?
    • 谢谢,我认为单独的数据库和SCAN 的组合确实可以在这种情况下工作。感谢您的回答!
    猜你喜欢
    • 1970-01-01
    • 2015-03-19
    • 2017-08-22
    • 1970-01-01
    • 2012-12-22
    • 1970-01-01
    • 1970-01-01
    • 2014-02-25
    • 2012-12-10
    相关资源
    最近更新 更多