17

我在我的应用程序中使用 Passport 进行身份验证,并且我也在使用 Express。总结一下我的问题:我的登录功能最初运行良好,但是在任何用户的会话超时后,没有用户能够登录。

我正在使用标准的本地策略进行身份验证。

我将根据我的设置包含尽可能简单的示例:

//-------------
//Set up authentication with Passport
//-------------
var userModel = require('./models/user')(db);
passport.use(new LocalStrategy(
    function(username, password, done) {
        var errorMessage = 'Incorrect username/password combination.';
        userModel.GetUserByUsername(username, function(err, user) {
            if (err) { return done(err); }
            if (!user) {
              return done(null, false, { message: errorMessage });
            }

            user.validatePassword(password, function(isPasswordCorrect) {
                if (!isPasswordCorrect)
                {
                    return done(null, false, { message: errorMessage });
                }

                //Update with login date
                userModel.UpdateUserWithLogin(username, user.currentLoginTime, function(err){
                    //if we have an error here, we should probably just log it
                    if(err)
                    {
                        console.log(err);
                    }
                });

                return done(null, user);
            });
        });
    }
));

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

passport.deserializeUser(function(user, done) {
    userModel.GetUserByUsername(user._id, function(err, user) {
            done(err, user);
        });
});

//-------------
//Set up express and configure
//-------------
var sessionStore = new SkinStore(db);
var app = express();

app.configure(function(){
    app.set('port', process.env.PORT || 3000);
    app.set('views', __dirname + '/views');
    app.engine('html', consolidate.swig);
    app.set('view engine', 'html');
    swig.init({
        root: '.',
        allowErrors: true, // allows errors to be thrown and caught by express instead of suppressed
        autoescape: false});

    app.use(express.logger('dev'));

    app.use(express.bodyParser());
    app.use(express.methodOverride());
    app.use(express.cookieParser("[mysecrethere]"));
    app.use(express.session({   store: sessionStore,
                            cookie: { expires : new Date(Date.now() + 3600000) } //1 Hour
                            }));
    app.use(passport.initialize());
    app.use(passport.session());
    app.use(flash());
    app.use(expressValidator);

    app.use(express.static(path.join(__dirname, 'public')));

    //Dynamic helpers
    app.use(require('./helpers/DynamicHelpers'));

    app.use(app.router);
});

app.get('/login', routes.login);
app.post('/login', passport.authenticate('local', {failureRedirect: '/login',
                                               badRequestMessage: "Please enter username and password",
                                               failureFlash: true }),
                                               function(req, res) {
                                                    var targetUrl = req.session.pageAfterLogin;
                                                    delete req.session.pageAfterLogin;
                                                    res.redirect(targetUrl || '/account');
                                                });

app.get('/account', IsAuthenticated, routes.account.show);

和 IsAuthenticated 辅助函数:

function IsAuthenticated(req,res,next){
    if(req.isAuthenticated())
    {
        next();
    }
    else
    {
        //save the requested page and then redirected
        req.session.pageAfterLogin = req.url;
        req.flash("error", "You must be logged in first!");
        res.redirect('/login');
    }
}

我可以通过调试发现,在成功验证后(并且在 cookie 过期后),我点击了这个逻辑(从上面):

function(req, res) {
    var targetUrl = req.session.pageAfterLogin;
    delete req.session.pageAfterLogin;
    res.redirect(targetUrl || '/account');
}

我可以看到“req”正确设置了会话,并正确存储了 Passport 信息。然后,重定向发生,请求没有存储会话信息,并且具有全新的会话 ID。我怀疑客户端上没有设置 cookie,而且似乎确实如此,这应该可以解释缺乏一致的会话。

但是,我无法弄清楚为什么没有设置新的 cookie。应用程序的配置方式是否有问题表明为什么会发生这种情况?

我应该补充一点,重新启动 Node.js 实例可以解决问题,这在生产中不是可以容忍的。

谢谢。

更新:我运行 Fiddler 来查看 HTTP/S 流量发生了什么,我可以看到当它最初工作时,我在浏览器中设置了一个 cookie(我尝试了几个),然后将其传递回服务器后续请求。

当它不起作用时,浏览器不会将 cookie 传递给服务器,因此 Node 会发送一个 Set-Cookie 标头,每次都提供一个新的 cookie。到目前为止,我还没有运气确定造成这种情况的原因。

4

2 回答 2

29

我想通了,虽然我不喜欢这个答案。

tl;博士; - 使用 maxAge 而不是 expires。


问题的根源在于每个 cookie 上设置的到期日期(由 Express 自动设置)。我注意到设置的每个 cookie 都有相同的到期日期,最终最终成为过去,因此立即到期。

原因就在这里:

cookie: { expires : new Date(Date.now() + 3600000) }

新日期仅在服务器启动时创建一次。这导致每次的到期日期都相同。根据原始帖子中的代码,我无法弄清楚为什么它不起作用,但我在网上找到的每个示例都使用完全相同的代码。我通过定义一个创建此日期的函数来验证这一点,并检查它是否仅在服务器启动时被调用。

为了解决这个问题,我定义了 maxAge 而不是“expires”。maxAge 需要几毫秒,而不是日期,并且它似乎正确地设置了所有 cookie 的到期日期。

我很想听听是否有人可以首先解释为什么会发生这种情况,因为其他人似乎成功地使用了它。有什么想法吗?

请参阅下面的我的工作代码

app.configure(function(){
    app.set('port', process.env.PORT || 3000);
    app.set('views', __dirname + '/views');
    app.engine('html', consolidate.swig);
    app.set('view engine', 'html');
    swig.init({
        root: '.',
        allowErrors: true, // allows errors to be thrown and caught by express instead of suppressed
        autoescape: false});

    app.use(express.logger('dev'));

    app.use(express.bodyParser());
    app.use(express.methodOverride());
    app.use(express.cookieParser("[mysecrethere]"));
    app.use(express.session({   store: sessionStore,
                            cookie: { maxAge : 3600000 } //1 Hour
                            }));
    app.use(passport.initialize());
    app.use(passport.session());
    app.use(flash());
    app.use(expressValidator);

    app.use(express.static(path.join(__dirname, 'public')));

    //Dynamic helpers
    app.use(require('./helpers/DynamicHelpers'));

    app.use(app.router);
});
于 2013-02-28T05:14:33.940 回答
-2

将 cookie 名称设置为 value,其中可能是转换为 JSON 的字符串或对象。路径选项默认为“/”。

res.cookie('rememberme', '1', { expires: new Date(Date.now() + 900000), httpOnly: true });

maxAge 选项是一个方便的选项,用于设置相对于当前时间的“过期”(以毫秒为单位)。以下与前面的示例等效。

res.cookie('rememberme', '1', { maxAge: 900000, httpOnly: true })

还有链接

http://expressjs.com/api.html#res.cookie

于 2013-08-21T09:27:51.963 回答