JWT认证异常:关闭标签页后仍可访问受保护页面求助
问题分析与解决方案
核心问题
- 会话Cookie生命周期误解:登录时设置的JWT Cookie属于会话Cookie(未指定
expires或maxAge),这类Cookie仅在整个浏览器进程关闭时才会失效,单个标签页关闭不会清除Cookie,因此新标签页请求仍会携带Cookie通过认证。 - HTTPOnly Cookie可见性误解:由于Cookie设置了
httpOnly: true,前端通过document.cookie无法看到该Cookie,但浏览器在发送请求时会自动携带,导致你误以为Cookie不存在,实际认证仍能通过。 - 登录逻辑判断错误:
if(findData==={})永远为false,对象引用对比无法匹配空对象,应改用if(!findData)判断用户是否存在。 - 中间件错误操作:认证通过后执行
res.cookie('jwt',' ',{maxAge:1})会立即清空Cookie,导致用户仅能访问一次受保护页面,不符合需求。
修复步骤
1. 修正登录逻辑的判断错误
修改登录接口中的用户存在判断,并配置Cookie实现标签页关闭失效:
app.post('/login/recorded',async(req,res)=>{ const findData=await RegisterationModel.findOne({email:req.body.email}); try{ console.log(findData); // 替换原错误判断,正确检测用户是否存在 if(!findData){ res.send("not found"); return; // 提前返回,避免后续代码执行 } if(findData.email===req.body.email && findData.password===req.body.password){ const token = await findData.generateAuthToken(); res.cookie('jwt',token,{ httpOnly: true, // 配置Cookie与标签页绑定,关闭标签页自动失效(Chrome 104+支持) sameSite: 'Strict', partitioned: true, // 生产环境需开启secure(仅HTTPS下生效) secure: process.env.NODE_ENV === 'production' }); res.redirect('/index'); console.log(token) } else{ res.send("invalid"); } } catch(error){ console.log(error); } })
2. 修正认证中间件的错误操作
移除中间件中清空Cookie的代码,确保登录后可正常访问受保护页面:
const auth =async(req,res,next)=>{ try{ console.log("auth") const token=req.cookies.jwt; // 主动检测token是否存在,提前抛出错误 if(!token){ throw new Error('未提供认证Token'); } const verifyUser=jwt.verify(token,'helloworld'); const user=await RegisterationModel.findOne({_id:verifyUser._id}); console.log(verifyUser); // 移除清空Cookie的错误代码 next(); } catch(error){ res.status(401).send(error.message || '认证失败'); console.log(error) } }
3. 兼容其他浏览器的可选方案
若需兼容不支持partitioned属性的浏览器,可结合前端sessionStorage做辅助校验:
- 在
index.hbs中添加前端脚本:<html> <head> <title>Index Filee</title> </head> <body> <ul> <li><a href="/secretpage">Secret Page</a></li> <li><a href="/login">Login</a></li> <li><a href="/register">Register</a></li> </ul> <script> // 生成标签页唯一认证密钥 if (!sessionStorage.getItem('tabAuthKey')) { sessionStorage.setItem('tabAuthKey', Math.random().toString(36).substring(2)); } // 拦截所有请求,携带密钥到后端(示例用fetch拦截) const originalFetch = window.fetch; window.fetch = function(url, options = {}) { options.headers = options.headers || {}; options.headers['X-Tab-Auth-Key'] = sessionStorage.getItem('tabAuthKey'); return originalFetch(url, options); }; </script> </body> </html> - 中间件新增密钥校验逻辑(需配合服务器存储关联关系,如Redis):
const auth =async(req,res,next)=>{ try{ console.log("auth") const token=req.cookies.jwt; const tabKey=req.headers['x-tab-auth-key']; if(!token || !tabKey){ throw new Error('未提供完整认证信息'); } // 此处需从存储中校验tabKey与用户的关联关系,示例省略存储逻辑 const verifyUser=jwt.verify(token,'helloworld'); const user=await RegisterationModel.findOne({_id:verifyUser._id}); console.log(verifyUser); next(); } catch(error){ res.status(401).send(error.message || '认证失败'); console.log(error) } }
验证效果
- 登录成功后,可正常访问
/secretpage。 - 关闭当前标签页,重新打开新标签页访问
/secretpage,浏览器会因无有效Cookie(或密钥校验失败)返回401认证错误,无法访问受保护页面。
内容的提问来源于stack exchange,提问作者mahir
相关产品推荐
相关产品推荐

