极少数情况下,用户会话在注销时不会被删除。为什么会发生这种情况?

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% 的时间,但这就是它的工作方式。