Cookie 与 sessionStorage 的区别与用法,解决浏览器多开,cookie覆盖问题。
Cookie 与 sessionStorage 的区别与用法
今天在写项目的时候测试发现个bug,应该是全栈萌新都会遇到。业务场景如下:
当前一个浏览器,同时登录10个不同的账号,访问你的服务器应用,其中有一个退出了,其他的用户都退出了。这是个很明显的bug,之前做的时候只是一个浏览器测试没发现这个问题。产生这个原因如下: 前端请求拦截和响应,在做处理的时候是基于cookie做的处理导致的,至于cookie的特点用法后边说。因为用了cookie在当前浏览器共享了信息导致的,cookie的作用域在同源(所有标签页/窗口共享),存储也是最久的。我在登录的时候把需要认证的东西放在cookie,直接在 Header 中读取 cookieconst userRoleCookie = useCookie('userRole')这样干导致的。所有后边考虑到了一个浏览器可能登录多个用户,必须隔离所以,做了隔离用sessionStorage解决了。那么sessionStorage的法就是解决上边的业务场景的,主要是做隔离,单页应用的。这两个没有谁好谁坏,不同场景需求不同。一、核心区别速查表
| 对比维度 | Cookie | sessionStorage |
|---|---|---|
| 存储大小 | 约 4KB | 约 5-10MB |
| 生命周期 | 可设置过期时间(Expires/Max-Age) | 标签页关闭即清除 |
| 作用域 | 同源(所有标签页/窗口共享) | 单个标签页(独立隔离) |
| 发送机制 | 每次 HTTP 请求自动携带(Cookie头) | 不会自动发送,需手动读取放在请求头 |
| 服务端访问 | ✅ 服务端可直接读取(HttpServletRequest) | ❌ 服务端无法直接读取 |
| 安全性 | 可设置HttpOnly防止 XSS 读取 | 易受 XSS 攻击(无 HttpOnly 选项) |
| 同标签页刷新 | ✅ 保留 | ✅ 保留 |
| 关闭浏览器 | ✅ 保留(未过期前) | ❌ 全部清除 |
| 跨标签页通信 | ✅ 可共享(被覆盖是共享的代价) | ❌ 不可共享(这正是隔离的优势) |
| 使用场景 | 服务端认证、会话标识、偏好设置 | 前端临时状态、单页应用隔离认证、多标签页独立登录 |
二、为什么项目需要从 Cookie 切换到 sessionStorage?
问题复现流程
| 步骤 | 使用 Cookie(❌ 错误) | 使用 sessionStorage(✅ 正确) |
|---|---|---|
| 用户A在标签页1登录 | Cookie 存入 authKey_A | sessionStorage(标签页1)存入 authKey_A |
| 用户B在标签页2登录 | Cookie 被覆盖为 authKey_B | sessionStorage(标签页2)独立存入 authKey_B |
| 用户A点击操作 | ❌ 请求携带 authKey_B,认证失败 | ✅ 请求携带 authKey_A,认证成功 |
| 用户B退出 | ❌ Cookie 被清除,标签页1丢失认证 | ✅ 仅清除标签页2的 sessionStorage,标签页1不受影响 |
一句话总结:Cookie 是浏览器级共享 → 多标签页不同用户会相互覆盖;sessionStorage 是标签页级隔离 → 各标签页用户互不干扰。
三、sessionStorage 在 Nuxt3 中的完整用法
1. 安装 VueUse(Nuxt3 推荐方案)
pnpmadd@vueuse/nuxt @vueuse/core配置nuxt.config.ts:
exportdefaultdefineNuxtConfig({modules:['@vueuse/nuxt']})2. 登录页面存储
<script setup> import { useSessionStorage } from '@vueuse/core' // 创建响应式引用,每个标签页独立 const authKey = useSessionStorage('authKey', '') const userRole = useSessionStorage('userRole', '') const userName = useSessionStorage('userName', '') const handleLogin = async () => { const response = await login(form) const { authKey: key, role, userName: name } = response.data // 存储到当前标签页 authKey.value = key userRole.value = role.toString() userName.value = name await router.push('/') } </script>3. 请求拦截器读取
// request.jsimport{useSessionStorage}from'@vueuse/core'request.interceptors.request.use((config)=>{constauthKey=useSessionStorage('authKey').valueif(authKey){config.headers['AuthKey']=authKey// 手动放入请求头}returnconfig})4. 退出登录清除
// 退出或 401 响应时constauthKey=useSessionStorage('authKey')constuserRole=useSessionStorage('userRole')constuserName=useSessionStorage('userName')authKey.value=nulluserRole.value=nulluserName.value=nullrouter.push('/login')四、Cookie 的正确用法(什么时候用 Cookie?)
当您需要服务端直接读取认证信息时,才用 Cookie:
// 服务端读取 Cookie@GetMapping("/user")publicUserDTOgetUser(HttpServletRequestrequest){Cookie[]cookies=request.getCookies();for(Cookiecookie:cookies){if("authKey".equals(cookie.getName())){StringauthKey=cookie.getValue();// 验证 authKey...}}}典型场景:
- 服务端 Session 管理
- 记住登录状态(
rememberMe) - 需要服务端直接读取的认证信息
但如果您用的是前后端分离 + Token(如 authKey 放在请求头),则不应该用 Cookie 存储 authKey,而应该用 sessionStorage。
五、最佳实践总结
| 存储位置 | 适合存储的内容 | 不适合存储的内容 |
|---|---|---|
| Cookie | 服务端需要读取的会话ID、偏好设置、rememberMe标识 | 敏感 Token(易被 XSS 窃取,除非设置HttpOnly) |
| sessionStorage | 前端认证 Token、当前标签页状态、临时表单数据 | 需要跨标签页共享的数据 |
| localStorage | 长期本地缓存、用户设置、主题偏好 | 任何敏感认证信息(永不过期,易被 XSS 窃取) |
| Pinia/Vuex | 全局状态管理(配合 sessionStorage 持久化) | 无(内存存储,刷新即失) |
六、您的项目改造清单
| 文件 | 修改内容 |
|---|---|
request.js | useCookie('authKey')→useSessionStorage('authKey') |
| 登录页面 | useCookie('authKey').value = key→authKey.value = key |
| 响应拦截器 401 | useCookie('authKey').value = null→authKey.value = null |
| 退出逻辑 | 同上,清除 sessionStorage |
| 后端 | 无需修改,请求头AuthKey不变 |
七、关键注意事项
useSessionStorage只在客户端可用,不要在服务端渲染时调用(Nuxt 中在onMounted或拦截器中使用是安全的)。useCookie和useSessionStorage在 Nuxt3 中的行为不同:useCookie是 Nuxt 内置的 SSR 友好 Cookie 管理useSessionStorage是 VueUse 提供的客户端存储封装
- 改造后,同一个浏览器的多个标签页可以独立登录不同用户,互不干扰。
- 关闭标签页后,sessionStorage 自动清除,无需手动清理(更安全)。
