极少数情况下,用户会话在注销时不会被删除。为什么会发生这种情况?
Very occasionally a user session is not deleted upon logout. Why might this happen?
环境: Node.js、Express、Express-session、Redis、Digital Ocean Droplet
背景:我的应用程序允许用户登录和注销他们创建的帐户。
问题:当用户注销时,可能有 1% 或 2% 的时间会话没有被破坏,用户可以通过简单地浏览到他们的 myAccount 页面它。这只发生在我的 Digital Ocean Droplet 上,从来没有发生在我的本地 Windows 机器上。我的 Windows 框仅用于测试并使用 express-session 提供的默认会话存储。
我可以用 redis-cli
确认会话偶尔不会被破坏。比如刚才,
$ redis-cli keys "*"
1) "sess:bVpK6dnOaMsF5ybU0fnnTsCXL14Y-fHh"
注销后,我再次 运行 redis-cli
,会话仍然存在。虽然我已被重定向到我的主页,但我可以浏览到我的帐户页面而无需再次登录。我单击注销,这次会话被破坏,我无法浏览到我的帐户。
我要强调的是,这几乎从未发生过。会话通常被销毁。为什么会发生这种情况?
这是我的 Droplet 的设置。
const session = require('express-session');
const redis = require('redis');
const redisClient = redis.createClient();
const RedisStore = require('connect-redis')(session);
app.set('trust proxy', 'loopback');
app.use(session({
name: process.env.SESSION_NAME,
proxy: true;
resave: true,
rolling: true,
saveUninitialized: true,
secret: process.env.SESSION_SECRET,
store: new RedisStore({ client: redisClient }),
unset: 'destroy',
cookie: {
maxAge: 2 * 60 * 60 * 1000,
sameSite: 'lax',
secure: true
}
}));
这就是我注销用户的方式。
router.get('/logout', redirectLogin, middlewareUserAccounts.logout);
exports.logout = wrapAsync(async function (req, res) {
req.session.destroy(function (err) {
if (err) {
throw new Error(err);
} else {
res.clearCookie(process.env.SESSION_NAME);
return res.redirect('/');
}
});
});
exports.redirectLogin = wrapAsync(async function (req, res, next) {
let doUserValuesExist = req.session.hasOwnProperty('userValues');
if (doUserValuesExist === false) return res.redirect('/login');
return next();
});
经过几个小时的研究和测试,我发现了问题的根源。
router.get('/logout', redirectLogin, middlewareUserAccounts.logout);
我使用了 GET
请求而不是 POST
请求。切勿使用 GET
将客户端从服务器注销,因为 GET
可能会使用浏览器中的缓存而不是访问服务器。在我的例子中,/logout
在会话被销毁后重定向到 /
。由于 /
在缓存中,它在不访问服务器的情况下提供该页面,并且会话没有被破坏。这在桌面上不是问题的原因是因为它没有使用本地文件的缓存。我不确定为什么这个问题在我的 Digital Ocean Droplet 上只发生 1% 或 2% 的时间,但这就是它的工作方式。
环境: Node.js、Express、Express-session、Redis、Digital Ocean Droplet
背景:我的应用程序允许用户登录和注销他们创建的帐户。
问题:当用户注销时,可能有 1% 或 2% 的时间会话没有被破坏,用户可以通过简单地浏览到他们的 myAccount 页面它。这只发生在我的 Digital Ocean Droplet 上,从来没有发生在我的本地 Windows 机器上。我的 Windows 框仅用于测试并使用 express-session 提供的默认会话存储。
我可以用 redis-cli
确认会话偶尔不会被破坏。比如刚才,
$ redis-cli keys "*"
1) "sess:bVpK6dnOaMsF5ybU0fnnTsCXL14Y-fHh"
注销后,我再次 运行 redis-cli
,会话仍然存在。虽然我已被重定向到我的主页,但我可以浏览到我的帐户页面而无需再次登录。我单击注销,这次会话被破坏,我无法浏览到我的帐户。
我要强调的是,这几乎从未发生过。会话通常被销毁。为什么会发生这种情况?
这是我的 Droplet 的设置。
const session = require('express-session');
const redis = require('redis');
const redisClient = redis.createClient();
const RedisStore = require('connect-redis')(session);
app.set('trust proxy', 'loopback');
app.use(session({
name: process.env.SESSION_NAME,
proxy: true;
resave: true,
rolling: true,
saveUninitialized: true,
secret: process.env.SESSION_SECRET,
store: new RedisStore({ client: redisClient }),
unset: 'destroy',
cookie: {
maxAge: 2 * 60 * 60 * 1000,
sameSite: 'lax',
secure: true
}
}));
这就是我注销用户的方式。
router.get('/logout', redirectLogin, middlewareUserAccounts.logout);
exports.logout = wrapAsync(async function (req, res) {
req.session.destroy(function (err) {
if (err) {
throw new Error(err);
} else {
res.clearCookie(process.env.SESSION_NAME);
return res.redirect('/');
}
});
});
exports.redirectLogin = wrapAsync(async function (req, res, next) {
let doUserValuesExist = req.session.hasOwnProperty('userValues');
if (doUserValuesExist === false) return res.redirect('/login');
return next();
});
经过几个小时的研究和测试,我发现了问题的根源。
router.get('/logout', redirectLogin, middlewareUserAccounts.logout);
我使用了 GET
请求而不是 POST
请求。切勿使用 GET
将客户端从服务器注销,因为 GET
可能会使用浏览器中的缓存而不是访问服务器。在我的例子中,/logout
在会话被销毁后重定向到 /
。由于 /
在缓存中,它在不访问服务器的情况下提供该页面,并且会话没有被破坏。这在桌面上不是问题的原因是因为它没有使用本地文件的缓存。我不确定为什么这个问题在我的 Digital Ocean Droplet 上只发生 1% 或 2% 的时间,但这就是它的工作方式。