问题
I've set up a working login test as follows:
var express = require('express');
var fs = require('fs');
var http = require('http');
var path = require('path');
var routes = require('./routes/index.coffee');
var passport = require('passport');
var LocalStrategy = require('passport-local').Strategy;
var User = require('./userdb.coffee');
var app = express();
var RedisStore = require('connect-redis')(express);
passport.use(new LocalStrategy(function(username, password, done) {
return User.findOne({
username: username
}, function(err, user) {
if (err) {
return done(null, false, message: error);
}
if (!user) {
return done(null, false, {
message: 'Unknown user'
});
}
if (!user.validPassword(password)) {
return done(null, false, {
message: 'Invalid password'
});
} else {
return done(null, user);
}
});
}));
passport.serializeUser(function(user, done) {
return done(null, user);
});
passport.deserializeUser(function(user, done) {
return done(null, user);
});
app.configure(function() {
app.set('port', process.env.PORT || 5003);
app.set('views', __dirname + '/views');
app.set('view engine', 'jade');
app.enable('trust proxy');
app.use(express["static"](path.join(__dirname, 'public')));
app.use(express.bodyParser());
app.use(express.methodOverride());
app.use(express.cookieParser('SOMESECRET'));
app.use(express.session({
cookie: {
maxAge: 10 * 60 * 1000
},
key: 'express.sid',
secret: 'SOMESECRET',
store: new RedisStore({
port: 6379,
host: 'localhost'
})
}));
app.use(passport.initialize());
app.use(passport.session());
return app.use(app.router);
});
var check_auth = function(req, res, next) {
if (req.isAuthenticated()) {
console.log(req.session.cookie.expires);
return next();
}
return res.redirect('/login');
};
app.get('/', check_auth, routes.index);
app.get('/login', routes.login);
app.get('/logout', routes.logout);
app.post('/authenticate', passport.authenticate('local', {
successRedirect: '/',
failureRedirect: '/login'
}));
app.listen(app.get('port'), '0.0.0.0');
The routes and User logic is omitted, as I take it they're irrelevant to my issue, but I'll happily share them if needed (they're very small, this is just to get a small proof of concept up and running).
Login works, reading the session works, rendering templates based on session values work. My problem is with maxAge/expires. I'm not sure where-in the problem lies, but I'll try to describe it:
When I log in, the session is saves with passport.js
, stored correctly in my RedisStore
, and a cookie pointing at the session is returned to the browser.
On subsequent requests the cookie is successfully found and points to the correct session from my SessionStore
. req.session.cookie
show an UPDATED expires
and in my redis
server, the TTL is reset to 10 minutes (600).
My problem is that the cookie remains at the same expirary in the browser (Chrome, windows and Mac).
So my question is: How can I debug this further?
As req.session is updated (by express
, passport
and connect-redis
internally/automatically), I'm wondering where the problem lies, and what I should do to fix this: Cookie remains at the initial maxAges/expires.
Any tips, pointers or ideas are grately appreciated.
回答1:
Express-session supports a rolling cookie expiration date. Unfortunately, it was only recently documented.
Use the "rolling" option for your session. This "forces a cookie set on every response" and "resets the expiration date." You want to set rolling to true.
Also pay attention to the "resave" option. That "forces session to be saved even when unmodified..." You'll likely want to set that option to true as well. Note that even though the default value is true for this option, you should set the value explicitly. Relying on the default for this option, rather than setting it explicitly, is now deprecated.
Try something like:
app.use( session( { secret: 'keyboard cat',
cookie: { maxAge: 60000 },
rolling: true,
resave: true,
saveUninitialized: false
}
)
);
Here's the documentation. Look under "Options" and "options.resave": https://github.com/expressjs/session .
回答2:
After some digging it turns out Express does not support this sort of rolling, and is left as an exercise for the programmer to implement.
It would help if the browsers expirary was reliably readable to express
, so you could bump the session only when it's close to expirary, but I use this as a workaround (inefficient) until I figure something smarter out:
check_auth = function(req, res, next) {
console.log(req.isAuthenticated());
if (req.isAuthenticated()) {
if (req.session.roll) {
req.session.roll = 0;
} else {
req.session.roll = 1;
}
return next();
}
return res.redirect('/login');
};
Where roll
could be anything, the point being the session is changed (on every auth-checked request*).
*) which also means it's wildly inefficient, but it will do for now.
One alternative could be to lookup the TTL of the session id. This would have to be checked in a way like: if ttl < 10% * maxAge (as defined by the app), as the TTL is actually correctly updated on every request, it's just that Set-Cookie isn't sent. As such, say the user stays within the 90% of maxAge, his browser-cookie will eventually expire, so even that approach is not sufficient. It could be a good middleground though.
I'll leave the question unaccepted, to encourage others to weigh in with better solutions.
来源:https://stackoverflow.com/questions/13090177/updating-cookie-session-in-express-not-registering-with-browser