Node.js + express.js + passport.js:在服务器重新启动时保持身份验证

76
我使用passport.js来处理我的nodejs + express.js应用的身份验证。我设置了一个LocalStrategy来从mongodb获取用户。
我的问题是,当我重新启动我的node服务器时,用户必须重新进行身份验证。这是一个问题,因为我正在积极开发它,并不想在每次重启时都登录...(另外我使用的是node supervisor)
这是我的应用程序设置:
app.configure(function(){
    app.use('/static', express.static(__dirname + '/static'));
    app.use(express.bodyParser());
    app.use(express.methodOverride());
    app.use(express.cookieParser());
    app.use(express.session({secret:'something'}));
    app.use(passport.initialize());
    app.use(passport.session());
    app.use(app.router);
});

并且会话序列化设置:

passport.serializeUser(function(user, done) {
    done(null, user.email);
});

passport.deserializeUser(function(email, done) {
    User.findOne({email:email}, function(err, user) {
        done(err, user);
    });
});

我尝试了一个博客(链接已不存在)上使用connect-mongodb提供的解决方案,但没有成功。

app.use(express.session({
    secret:'something else',
    cookie: {maxAge: 60000 * 60 * 24 * 30}, // 30 days
        store: MongoDBStore({
        db: mongoose.connection.db
    })
}));

编辑 附加问题: 只能建立一个连接 (使用mongohq免费服务限制)

编辑2 解决方案 (作为一次编辑,因为我的声望还不够高,不能回答我的问题)

这是我最终找到的解决方案,使用mongoose初始化连接

app.use(express.session({
    secret:'awesome unicorns',
    maxAge: new Date(Date.now() + 3600000),
    store: new MongoStore(
        {db:mongoose.connection.db},
        function(err){
            console.log(err || 'connect-mongodb setup ok');
        })
}));

只是提醒一下:那个博客链接把我重定向到了一个不良的约会网站...不知道它还好不好用。 - anthonylawson
1
好的,我已经删除了链接。 - Arnaud Rinquin
14
开发者制作了糟糕的约会网站。 - Joseph Yaduvanshi
我有一个类似相反的问题。当浏览器重新启动后,用户需要重新进行身份验证。你遇到过这个问题吗? - sasha.sochka
1
默认情况下,cookie.maxAge为null,表示未设置“过期”参数,因此该cookie将成为浏览器会话cookie。当用户关闭浏览器时,cookie(和会话)将被移除。请参阅文档以获取更多信息:https://github.com/expressjs/session - Arnaud Rinquin
7个回答

56

有一个开源项目叫做connect-mongo,它可以实现你所需的功能 - 将会话数据持久化到mongodb中。

使用示例(重复使用已经打开的mongoose连接):

var session = require('express-session');
var MongoStore = require('connect-mongo')(session);
var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/sess');
app.use(express.session({
    secret:'secret',
    maxAge: new Date(Date.now() + 3600000),
    store: new MongoStore(
    // Following lines of code doesn't work
    // with the connect-mongo version 1.2.1(2016-06-20).
    //    {db:mongoose.connection.db},
    //    function(err){
    //        console.log(err || 'connect-mongodb setup ok');
    //   }
    {mongooseConnection:mongoose.connection}
    )        
}));

您可以在这里阅读更多关于它的信息:https://github.com/kcbanner/connect-mongo


1
Arnaud,谢谢,我也遇到了同样的问题。您能否更明确地说明db:mongoose.connection.db是什么意思?我不确定它的含义。 - Jacques Tardie
抱歉回复晚了,我正在从墨西哥回到法国,48小时后我会更加可用。回复:好的,你必须在mongoose初始化之后将mongo连接实例传递给connect-mongodb。mongoose.connection.db就是我们所说的实例,我们通过“db”属性将其传递给connectdb。 - Arnaud Rinquin
请看这里:https://github.com/ArnaudRinquin/LostAndFound/blob/master/LostAndFound.js请注意,这是一段旧代码,可能需要适应新的API。 - Arnaud Rinquin
4
{db: mongoose.connection.db}无法工作。您可以使用{mongooseConnection: mongoose.connection},它确实可以工作。 - lmaooooo

6

我使用connect-mongo如下:

var MongoStore = require('connect-mongo');

var sess_conf = {
  db: {
    db: mydb,
    host: localhost,
    collection: 'usersessions' // optional, default: sessions
  },
  secret: 'ioudrhgowiehgio'
};

 app.use(express.session({
    secret: sess_conf.secret,
    maxAge: new Date(Date.now() + 3600000),
    store: new MongoStore(sess_conf.db)
  }));


[...]

// Initialize Passport!  Also use passport.session() middleware, to support
  // persistent login sessions (recommended).
  app.use(passport.initialize());
  app.use(passport.session());

谢谢,但我有一个额外的问题:我只想在我的数据库上拥有一个连接,因为我使用mongohq免费服务(仅限一个连接)。 - Arnaud Rinquin
好的,这是最终的、干净的解决方案,使用connect-mongodb并重用mongoose连接。 - Arnaud Rinquin

5
这是因为您在会话中使用了MemoryStore(默认)。看一下Connect框架中memory.js的代码:
var MemoryStore = module.exports = function MemoryStore() {
  this.sessions = {};
};

下面是 session.js(Express)中的代码片段:

function session(options){
    /* some code */
    , store = options.store || new MemoryStore
    /* some code */
}

现在你应该明白,每次服务器重启都会重置MemoryStore。为了保留数据,您需要使用其他会话存储。您甚至可以编写自己的(不应该太困难),尽管Redis(请参见此库)可能是一个很好的选择(并且它得到了Express的良好支持)。
// 编辑
根据Connect文档,如果您实现getsetdestroy方法,则足以满足您的需求。以下代码应该可以工作:
customStore = {
    get : function(sid, callback) {
        // custom code, for example calling MongoDb
    },
    set : function(sid, session, callback) {
        // custom code
    },
    destroy : function(sid, callback) {
        // custom code
    }
}    

app.use(express.session({
    store: customStore
}));

你只需要实现调用MongoDb(或任何其他数据库,尽管我仍然建议使用像Redis这样的非永久性数据库)来存储会话数据。同样阅读其他实现的源代码以掌握思路。

1
谢谢。正如您所看到的,我尝试使用connect-mongodb(和connect-mongo),但没有成功,会话没有保存在我的数据库中。 - Arnaud Rinquin
1
@ArnaudRinquin 抱歉,不知何故我没有看到。 :) 我已更新答案。我对 connect-mongodb 和 connect-mongo 都不熟悉(我正在使用 Redis 作为会话存储的 mongoose),所以我唯一能给你的建议是:尝试自己实现会话存储。 - freakish
谢谢你的帮助,但我几分钟前已经找到了解决方案,并在想发布解决方案时看到了你的回复。 - Arnaud Rinquin
1
@ArnaudRinquin 你知道你可以回答自己的问题并将其标记为已接受吗?因为说实话,我的回答帮助不大。 :) - freakish
我知道,但我的声誉太低了。我会尽快完成。谢谢你的诚实。 - Arnaud Rinquin

2

对于有经验的Node用户来说,这可能很明显,但对我来说确实会有些困惑:

您需要配置Node会话 - 例如:

app.use(session({secret: "this_is_secret", store: ...}));

在初始化护照会话之 - 例如。

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

如果您首先调用passport.session(),它将无法正常工作(也不会警告您)。我曾认为问题出在序列化/反序列化用户函数上,浪费了数小时。

1
我最终做的事情:
var expressSession = require('express-session');
var redisClient = require('redis').createClient();
var RedisStore = require('connect-redis')(expressSession);
...
app.use(expressSession({
    resave: true,
    saveUninitialized: true,
    key: config.session.key,
    secret: config.session.secret,
    store: new RedisStore({
        client: redisClient,
        host: config.db.host,
        port: config.db.port,
        prefix: 'my-app_',
        disableTTL: true
    })
}));

适用于我。

1

你需要更改用于会话的存储方式。默认的存储方式'MemoryStore'在应用程序停止后不会继续存储会话。请查看GitHub上的express-session,了解其他存储方式,例如mongo。 (我记不清名称了)


1

我正在使用mongoose,我尝试了上面答案中提供的代码,但对我没有起作用。当我执行时,出现了以下错误:

Error: db object already connecting, open cannot be called multiple times

然而,这对我有效:
app.use(express.session({
    secret:'secret',
    maxAge: new Date(Date.now() + 3600000),
    store: new MongoStore({mongoose_connection:mongoose.connection})
}))

注意:如果由于某种原因您没有MongoStore,您需要执行以下操作:
npm install connect-mongo --save

然后:
var MongoStore = require('connect-mongo')(express)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接