Node.js中如何防止用户登出后点击浏览器后退进入受保护页面
问题根因
这个现象和服务端session销毁逻辑没有关系,是浏览器*往返缓存(Back/Forward Cache,简称bfcache)*机制导致的:
- 用户点击浏览器后退按钮时,浏览器会直接从本地缓存读取之前已经渲染完成的受保护页面,不会向服务端发起新的HTTP请求
- 服务端配置的未登录重定向鉴权逻辑因为没有收到请求,根本不会触发,才会出现用户能看到受保护页面的情况
- 直接输入URL访问时会发起真实服务端请求,鉴权逻辑正常执行,所以表现符合预期。
解决方案
1. 给所有受保护路由加禁用缓存响应头(核心方案)
通过中间件给所有需要登录才能访问的接口/页面设置强禁用缓存的响应头,强制浏览器在访问这些页面(包括后退触发的访问)时,必须重新向服务端发起请求走鉴权逻辑。
Express 中可以直接写一个通用中间件挂载到受保护路由上:
// 禁用缓存中间件 const disablePageCache = (req, res, next) => { res.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, proxy-revalidate'); res.setHeader('Pragma', 'no-cache'); res.setHeader('Expires', '0'); next(); }; // 挂载到所有受保护路由,比如管理后台、个人中心类路由都需要添加 app.use('/user/*', disablePageCache, authRequired, userRoutes); app.use('/admin/*', disablePageCache, authRequired, adminRoutes);
其中Cache-Control: no-store是最核心的配置,会明确告知浏览器不要存储该响应的任何副本,任何场景下访问都必须回源请求服务端。
2. 补全登出逻辑的遗漏步骤
只销毁服务端session、不清理客户端存储的session cookie,会放大缓存带来的异常问题,标准登出逻辑参考:
app.post('/logout', (req, res) => { // 销毁服务端session req.session.destroy((err) => { if (err) return res.sendStatus(500); // 清理客户端存储的session cookie,express-session默认cookie名是connect.sid,自定义过cookie名称则替换为实际值 res.clearCookie('connect.sid'); // 登出成功后跳转到登录页 res.redirect('/login'); }); });
3. 前端加兜底校验(兼容极端场景)
针对部分对缓存头兼容不好的旧浏览器,可以在受保护页面加一段轻量校验逻辑,监听页面从bfcache恢复的事件,触发时主动校验登录态:
// 受保护页面内嵌的前端脚本 window.addEventListener('pageshow', (event) => { // 判断页面是否是从往返缓存加载的 const isBfCacheLoad = event.persisted || performance.getEntriesByType('navigation')[0]?.type === 'back_forward'; if (isBfCacheLoad) { // 发轻量请求校验登录态 fetch('/api/auth/check', { credentials: 'include' }) .then(res => { if (!res.ok) { // 未登录直接替换地址到登录页,不保留历史记录 window.location.replace('/login'); } }); } });
注意:不要只靠前端写meta标签实现禁用缓存,这类标签优先级低于HTTP响应头,在多数现代浏览器里不生效。
内容的提问来源于stack exchange,提问作者Muhmd Ajeer
相关产品推荐
相关产品推荐

