express - 如何从不同的浏览器永久保持登录?

标签 express redis express-session

背景:

我有一个应用程序(node、expressjs、express-session、redis),尽管没有设置 maxAgeexpires,但似乎在一段时间后登录超时。

意图:

通过管理面板,我希望管理员能够查看当前 session (已经在工作,通过从 redis 获取 sess:*),并单击 session 上的持久按钮以使其持续有效无限期。

我需要一种可靠的方法,将针对 sess:... 存储的数据转换为可以引用浏览器实例的唯一标识符。可能是在登录时存储已签名的内容,然后将其保存到永久数据库中,该数据库与 session 检查一起进行检查(并在需要时设置新的经过身份验证的 session )。

编辑:为了澄清“来自不同的浏览器” - 这是用于管理功能的。因此,目标是获得当前 session 的列表(已实现),每个 session 上都有一个“保留此登录”按钮。

这意味着相关用户不会注销。

编辑:这是我当前的 session 处理代码:

const express = require('express') // v4.17.1
const app = express()
const redis = require('redis') // v4.5.1
const expressSession = require('express-session') // v1.17.2
const RedisStore = require('connect-redis')(expressSession) // v3.4.2
const redisClient = redis.createClient({ legacyMode: true })
redisClient.connect()
const session = expressSession({
    store: new RedisStore({ client: redisClient }),
    saveUninitialized: true,
    secret: ‘xxxxxxx’,
    resave: true
})
app.use(session)

最佳答案

回答第一部分:

How can I persist a login forever, from a different browser? ... I have an app (node, expressjs, express-session, redis) which, despite not setting maxAge or expires, seems to timeout logins after a while.

如果您没有设置express-session cookie.maxAgecookie.expires,那么redis-connect将设置默认的TTL有一天:

If the session cookie has a expires date, connect-redis will use it as the TTL. Otherwise, it will expire the session using the ttl option (default: 86400 seconds or one day). -- https://github.com/tj/connect-redis#ttl

您可以通过设置 redis-connect disableTTL 来禁用它选项为真。不建议您这样做(请参阅下一部分)。

(请注意,也可能是完全不同的情况,例如您的用户在关闭浏览器时删除您的 Cookie,或者您有 SPA 并且 session 在刷新时无法正确跟踪等)。

回答第二部分:

总结我对你想要的东西的理解:

  • 一个用户可以有多个登录 session ,每台设备 1 个,最多 N 个事件 session (N 是什么或它如何施加超出了范围)。
  • 您希望使用假设的管理面板上的这个假设的按钮来动态地创建一些永久 session 。

因此,您似乎想要做的是唯一地标识每个用户的设备,并能够在默认永远之间动态切换给定设备的 session 生命周期。重要的是设备 ID, session ID 在这里是偶然的。

如果你想使用express-session来做到这一点,我建议不要尝试使用disableTTL,而只是设置一个非常大的cookie.maxAge 这些 session (在网络应用程序中,5 年实际上是永远的,并且您可以在用户每次处于事件状态时触摸 session 来重置 TTL)。

下面是执行此操作的概念验证代码。请注意,代码在撰写本文时使用最新版本 - 可能会与其他版本发生冲突。您需要填写详细信息:

  1. 在您的用户数据库中存储指示哪些设备是永久设备的标志。
  2. 弄清楚如何唯一地标识用户登录的每个设备(我建议只对设备类型进行分组,并且每种类型只允许一个 session ,但我不太了解您想要做什么,无法提供更具体的建议)。
  3. 当管理员切换管理面板或其他位置上的按钮时更新事件 session 。

const express = require('express');
const redis = require('redis');
const session = require('express-session'); // https://github.com/expressjs/session
const app = express();
const port = 3000;
const RedisStore = require('connect-redis')(session);
const redisClient = redis.createClient({
  url: 'redis://localhost',
  legacyMode: true,
});
redisClient.connect()
  .then(() => { console.log('Redis client connected'); })
  .catch(console.error);

const DAY_IN_MS = 60 * 60 * 24 * 1000;
const YEAR_IN_MS = 365 * DAY_IN_MS;

// Mock user DB.
const users = { 
  bob: {
    id: 'bob',
    forever_devices: ['foo_device', 'bah_device']
  }
};

app.use(session({
  store: new RedisStore({
    client: redisClient,
    // disableTTL: true, // Dont need this.
  }),
  secret: 'keyboards',
  saveUninitialized: false,
  resave: true,
  cookie: {
    maxAge: 3*DAY_IN_MS // Default 3 days.
  },
}));
app.listen(port, () => console.log(`App listening on port ${port}!`));

app.use((req, res, next) => {
  console.log(req.query, req.sessionID, req.session);
  next();
});

// Mock login accepting a mock user id and device_id. Obviously insecure ..
app.get('/login', (req, res) => {
  const { id, device_id } = req.query;
  if(!(id in users)) {
    return res.send('Denied').status(401);
  }
  req.session.loggedIn = true;
  if(users[id].forever_devices?.includes(device_id)) {
    req.session.cookie.maxAge = YEAR_IN_MS*1000;
  }
  res.send('OK');
});

app.get('/logout', (req, res) => {
  req.session.destroy();
  res.send('OK');
});

app.get('/', async function (req, res) {
  if(req.session.loggedIn) {
    redisClient.v4.ttl(`sess:${req.sessionID}`).then((d) => {
      res.send(`Hi ${req.sessionID} your TTL is ${d}`);
    });
  } else {
    res.send('Not logged in');
  }
});

使用curl进行测试(您需要在没有凭据的情况下在本地主机上启动redis):

> curl -b cjar -c cjar 127.0.0.1:3000/login?id=bob
OK
> curl -b cjar -c cjar 127.0.0.1:3000/
Hi sukXf3ddjx3AMakVGIfChvlI-_KhaPqQ your TTL is 259196
> curl -b cjar -c cjar 127.0.0.1:3000/logout
OK
> curl -b cjar -c cjar "127.0.0.1:3000/login?id=bob&device_id=foo_device"
OK
> curl -b cjar -c cjar 127.0.0.1:3000/
Hi 81JZL-PxCRhNclktuYxHjTeHibaoNP9G your TTL is 31535999997
> curl -b cjar -c cjar 127.0.0.1:3000/logout
OK
> curl -b cjar -c cjar 127.0.0.1:3000/
Not logged in

关于express - 如何从不同的浏览器永久保持登录?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/74777255/

相关文章:

python - 如何在 Django 中使用 redis?

caching - Redis 如何知道它是否必须从数据库返回缓存数据或新数据

lua - 使用本地属性的 redis.call 错误

node.js - Express session 和 PassportJS 之间的区别

javascript - 在expressjs中的回调之间传递路由参数的最佳方法

node.js - PM2 的高 HTTP 延迟

node.js - Redis 客户端连接到 redis 远程服务器,但无法在其上读取或写入

node.js - 为什么除了 passport.js 之外还要使用 cookie-session?

node.js - 使用快速 session 时何时创建 cookie?

node.js - 如何使用 morgan 记录器?