0

我正在尝试使用 passport.js 和 express-session 实现登录功能,效果很好,

var passport = require('passport'),
    session = require('express-session'),
    MongoDBStore = require('connect-mongodb-session')(session);

var sessionStore = new MongoDBStore({
    uri: config.db,
    collection: 'sessions'
});

var sessionOptions = {
    name: 'very-secure-cookie',
    secret: config.secret,
    resave: false,
    saveUninitialized: true,
    cookie: {
        secure: true,
        maxAge: null
    },
    store: sessionStore
};

app.use(session(sessionOptions));
app.use(passport.initialize());
app.use(passport.session());

在这里,maxAgeisnull意味着客户端在关闭浏览器窗口时会被注销。但是,我想在客户的电子邮件地址中存储一个额外的 cookie,以便在他们稍后再来时预先填写<input type="text" name="email">他们想要重新登录的时间。

我尝试app.use()了另一个会话对象,只是为了将电子邮件信息存储在另一个 cookie 中,但由于某种原因,cookie 只设置了一次(第一个)。

app.use(session(returningSessionOptions));

在任何地方都找不到任何明智的解决方案:(

哦,我不得不提一下,我考虑过将cookie-parser中间件与 一起使用express-session,但文档指出它可能会导致冲突

4

1 回答 1

5

由于您没有包含您使用的完整代码,我假设您使用res.cookie(name, value[, options]);or设置 cookie res.setHeader('set-cookie', serializedCookie);

set-cookie 标头似乎存在问题。添加多个 cookie 时,它​​们会附加到标题中,以逗号分隔,这在大多数当前浏览器中似乎不起作用。标头仅在第一个逗号之前被解释,因此仅保存第一个 cookie。在某些浏览器中,第一个等号和后面的分号之间的所有内容都用作第一个 cookie 的值。

可能的解决方案

我可以使用下面的代码重现旧版本的 Express (3.0.0) 的问题。使用 Express 4.13.4(以及 3.21.2)同样的代码运行良好。如果您使用的是旧版本的 Express,我建议您更新到最新版本,因为这应该可以解决问题。如果您已经在使用当前版本,请尝试该示例是否适合您。如果不是,请提供您的 express-app 发送的代码和标头。在某些应用程序更复杂的情况下,即使使用适用于示例代码的版本(我尝试过 Express 3.21.2),问题也存在。

如果您有任何理由不更新到最新版本或更新无法为您解决问题,则可以使用替代解决方案:

重现问题的代码

为了消除应用程序中其他代码可能产生的副作用,这是我用来重现您的问题的最小示例:

    var express = require('express');
    var session = require('express-session');
    var passport = require('passport');
    var app = express();

    app.use(session({
        secret: 'keyboard cat',
        resave: false,
        saveUninitialized: true
    }));

    app.use(passport.initialize());
    app.use(passport.session());

    app.get('/', function (req, res) {
        res.cookie('cookie', 'value');
      res.send('Hello World!');
    });

    app.listen(3000, function () {
      console.log('Example app listening on port 3000!');
    });

通过浏览器访问 express-app(运行 Express 3.0.x)时,会发送以下响应头:

    set-cookie: cookie=value; Path=/
    set-cookie: cookie=value; Path=/,connect.sid=s%3ARPkxiLfy0dbeBs5eILYBLK6Yh-sOjABS.ykNXCmaOsxtIAXV%2Ba9GCW6YnBi0cBhvZIGvwlj%2F%2Fsy0; Path=/; HttpOnly

使用 Express >=3.21.2 在这个例子中一切正常:

    set-cookie: cookie=value; Path=/
    set-cookie: connect.sid=s%3AR6TVTnUe3eyIvlpT0Hl5ikwcH_XMHXId.ouysyG5tGGekaVDxZMXJP4A8SJfsckLE4GZ3%2B1Eyd1o; Path=/; HttpOnly

替代修复

从 express-session 借用 setcookie 函数的代码来设置 cookie 可以为您解决问题,因为它会将包含所有 cookie 的数组写入标题,同时保留已添加到响应中的那些。为此,请安装 cookie 模块,在上面的代码中添加var cookie = require('cookie');并替换res.cookie('cookie', 'value');为以下行:

    var data = cookie.serialize('cookie', 'value');
    var prev = res.getHeader('set-cookie') || [];
    var header = Array.isArray(prev) ? prev.concat(data)
      : Array.isArray(data) ? [prev].concat(data)
      : [prev, data];

    res.setHeader('set-cookie', header);

现在将以下标头发送到浏览器并保存所有 cookie(在 MacOS 上使用 Google Chrome 49 测试):

    set-cookie:cookie=value
    set-cookie:cookie=value
    set-cookie:connect.sid=s%3Aa1ZPDmERtKwUaPjY__SrPtIrpYC7swQl.0KOs83RSmUTG%2FgPoyOLo4u5UFTjC89yS0Ch0ZVXWVo8; Path=/; HttpOnly

不幸的是,在使用 Express 3.0.0 时,第一个 cookie 的标头是重复的,根据 RFC6265 第 4.1.1 节不应该这样做:

服务器不应在具有相同 cookie 名称的同一响应中包含多个 Set-Cookie 标头字段。(有关用户代理如何处理这种情况,请参阅第 5.2 节。)

有用的资源

以下文章也可能有所帮助:

于 2016-03-30T13:46:32.017 回答