Session Cookie深度解析与前端开发中的常见问题处理

春萍 Dev 安全 阅读 916
赞 9 收藏
二维码
手机扫码查看
反馈

又踩坑了,Session Cookie居然丢了

最近在开发一个后台管理系统的时候,遇到一个特别诡异的问题:用户登录状态总是莫名其妙丢失。折腾了半天发现是Session Cookie出了问题。这里我踩了个坑,花了整整一天才解决,记录下来给大家避个雷。

Session Cookie深度解析与前端开发中的常见问题处理

事情是这样的,我在用Express做后端,前端用Vue 3写的单页应用。登录功能调用后端接口返回用户信息,同时设置了一个HttpOnly的Session Cookie。按道理说,这个Cookie应该自动带上每次请求的,但实际测试时发现,某些情况下它就是不带!尤其是生产环境部署到Nginx上之后,这个问题更明显。

排查过程真是折磨人

最开始我以为是前端的问题,检查了axios的配置:

// axios配置
import axios from 'axios'
const instance = axios.create({
  baseURL: 'https://jztheme.com/api',
  withCredentials: true // 这里要记得开启跨域凭证
})

export default instance

这里要注意,withCredentials必须设置为true,不然浏览器不会发送Cookie。我当时就忘了设置这个,导致调试了半天才发现问题。不过改完之后问题还是存在,这让我很抓狂。

后来试了下发现,原来是后端CORS配置有问题。我的Express代码最初是这样写的:

// 原始CORS配置
const cors = require('cors')
app.use(cors())

这里我踩了个大坑,默认的cors()配置不会允许携带凭证。正确的写法应该是:

// 正确的CORS配置
const cors = require('cors')
app.use(cors({
  origin: 'https://your-frontend-domain.com', // 生产环境要指定具体域名
  credentials: true // 允许携带凭证
}))

核心代码就这几行

经过一番折腾,最终确定完整的解决方案如下:

后端Express部分:

// session配置
const session = require('express-session')
app.use(session({
  secret: 'my-secret-key',
  resave: false,
  saveUninitialized: true,
  cookie: {
    httpOnly: true,
    secure: process.env.NODE_ENV === 'production', // 生产环境必须是true
    sameSite: 'none' // 跨站请求必须设置为none
  }
}))

// CORS配置
const cors = require('cors')
app.use(cors({
  origin: process.env.NODE_ENV === 'production' 
          ? 'https://your-frontend-domain.com'
          : 'http://localhost:3000',
  credentials: true
}))

前端Vue部分:

// axios配置
import axios from 'axios'
const instance = axios.create({
  baseURL: process.env.NODE_ENV === 'production'
           ? 'https://jztheme.com/api'
           : 'http://localhost:5000/api',
  withCredentials: true
})

export default instance

这里有几个重要细节

  • secure属性:生产环境下必须设置为true,否则Chrome会直接拒绝发送Cookie
  • sameSite属性:现代浏览器要求跨站请求必须显式设置为’none’
  • CORS的origin:不能使用通配符*,必须指定具体的前端域名
  • HTTPS:生产环境一定要用HTTPS,不然secure为true时Cookie无效

这里有个小插曲,改完这些配置后,本地开发环境反而出问题了。原因是本地一般用HTTP,而secure设为true后Cookie就不生效了。最后我通过判断环境变量解决了这个问题:

secure: process.env.NODE_ENV === 'production'

还有个小问题待解决

虽然主要问题解决了,但现在偶尔还会出现第一次登录失败的情况。我怀疑是Nginx代理层的配置问题,暂时还没完全搞明白。目前的临时解决方案是让前端重试一次登录请求:

// 登录重试逻辑
async function login(payload) {
  try {
    const res = await api.post('/auth/login', payload)
    return res.data
  } catch (error) {
    if (error.response.status === 401) {
      // 第一次失败重试一次
      return api.post('/auth/login', payload)
    }
    throw error
  }
}

以上是我踩坑后的总结

这次经历让我对Session Cookie的理解更深了。以前总觉得设置个HttpOnly就万事大吉,没想到还有这么多坑。尤其是现代浏览器的安全策略越来越严格,像sameSite这种新特性很容易被忽视。

如果你也遇到类似的问题,或者有更好的解决方案,欢迎在评论区交流。毕竟前端安全这块水挺深的,大家一起探讨才能进步嘛。

本文章不代表JZTHEME立场,仅为作者个人观点 / 研究心得 / 经验分享,旨在交流探讨,供读者参考。
发表评论

暂无评论