Node.js Экспресс-выпуск Cookie Паспорта
Я использую 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. Затем происходит перенаправление, запрос новый не имеет сохраненной информации о сеансе и имеет совершенно новый идентификатор сеанса. Я подозревал, что на клиенте не было установлено никакого файла cookie, и это похоже на то, что должно объяснять отсутствие последовательных сеансов.
Однако я не могу понять , почему не установлен новый файл cookie. Что-то не так, как настроено приложение, которое указывает, почему это происходит?
Я должен добавить, что перезапуск экземпляра Node.js исправляет проблему, это просто не то, что было бы допустимым в процессе производства.
Спасибо.
UPDATE. Я запустил Fiddler, чтобы узнать, что происходит с трафиком HTTP/S, и я вижу, что когда он работает изначально, я получаю набор cookie в браузере (я попробовал несколько), который затем передается обратно на сервер при последующих запросах.
Когда он не работает, браузер не передает файлы cookie на сервер, поэтому Node отправляет заголовок Set-Cookie, каждый раз предоставляющий новый файл cookie. До сих пор мне не удавалось определить причину этого.
Ответы
Ответ 1
Я понял это, хотя мне не нравится ответ.
ТЛ; дг; - используйте maxAge вместо истечения срока действия.
Проблема была внедрена в дату истечения срока действия, установленную для каждого файла 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);
});
Ответ 2
Задайте имя файла cookie для значения, где может быть строка или объект, преобразованный в JSON. Параметр пути по умолчанию имеет значение "/".
res.cookie('rememberme', '1', { expires: new Date(Date.now() + 900000), httpOnly: true });
Опция maxAge является удобной опцией для установки "expires" относительно текущего времени в миллисекундах. Следующий пример эквивалентен предыдущему примеру.
res.cookie('rememberme', '1', { maxAge: 900000, httpOnly: true })
Также ссылка
http://expressjs.com/api.html#res.cookie