终极拷问:用户的登录信息放哪里?
时间:2026-7-25 00:42 作者:独元殇 分类: 开发相关
多年的实践,有且只有一个答案: 应该放在 Cookie。
我是个搞 Web 出海(很适合一个人副业做)的,经常搞 SaaS ,对于这个状态储存,是真的有点东西。
是 httpOnly Cookie 这种:
Set-Cookie: Token=sk-ajkdhaskjdh; HttpOnly; Secure; SameSite=Lax; Path=/
HttpOnly 保证 JS 无法读取,只有服务器后端能读取。
Secure 确保只允许 HTTPS 可以读取。
SameSite=Lax 可以让属于其他服务器的后端请求,无法读取这个 Cookie (这个很重要,可以防止 CSRF (跨站请求伪造),但是很可惜,要注意!这个无法防止子域名.... 毕竟你的后端很多服务,是使用的子域名)。
最后面的 path 则限定了请求地址,比如 path: '/api/refresh', 。
但是,其实这种还不行,还不够安全,我们应该搞成 session !why?好问题!我下面会说哈哈。
Set-Cookie: session=user9527; HttpOnly; Secure; SameSite=Lax; Path=/
为什么它最好?
先说说 JWT ,也就是 JSON Web 令牌。
这是一个无状态的令牌,用户的 ID 和 签名 会被嵌在这一串字符串里(一般有效期是 7 天)。
然后我们很多人都是把它储存到 localstorage 里面,因为存 localstorage 要比存 Cookie 里更轻松一点,还没有过期时间。
但是!localstorage 很危险,因为页面上的任何 JS 代码,都可以读取 localstorage 里的任何内容。你会说:「我页面的代码都是自己人写的,我管好不就行了?」
哈哈,其实,也没那么简单。大部分前端页面还是依赖 npm 包的,而且你安装的 wp 插件,或者你浏览器插件等等你都没法控制。你站小可能还没人顶上,但站大了试试!就算你站很谨慎,但是还有 XSS 跨站脚本攻,击 ,尤其是评论区。很多评论区可以直接运行 html ,这个就比较危险了。
(当然,其实都能在你网站上运行任意 JS 脚本了,其实要不要令牌都无所谓了.... 但是,坏人拥有令牌,坏人干活确实会更自由一点)
JWT 有啥优点呢?更直接,更快。在一些比较安全的操作 API 里,比如就是发个通知,或者写个日志等等,让步骤简单很多。谷歌的认证登录就是这种。
第二种方式:存到内存里(也就是存到变量里)
这种方式确实,让能在你网站页面运行 JS 脚本的人更难得到你的令牌了,但是!你每次刷新还得重新搞,用户得重新登陆。而且跨页面,也没法把内存给跨着传输过去....
所以最好办法还是存到 httpOnly Cookie 这个里面。
session 是什么?
session 的中文是 会话ID ,它和 Token(JWT) 不一样。
我让 GPT 讲了个故事:
把网站想成一家酒店。 JWT 像一张写着“7 天有效”的通行证。酒店发出去后,保安只认这张证。哪怕你第二天被拉黑,只要证还没过期,理论上还能进去。 Session 更像房卡。卡上只有一个编号,身份、权限都在前台电脑里。你退房、被封禁、权限变化,前台一改,房卡立刻失效。
明白了吧,而且 JWT 往往非常的长,而 session 很短。在 数据库 查询和数据储存上,会节约不少时间(万一你有 10w 个用户,这个积累下来体积可不小)。
当然要注意,登录时务必生成全新的 session ,切勿重复使用客户端提供的旧 ID !这样也能防止被黑。
而且,每次进行一些敏感操作时,也要重新更新 session ,比如修改密码,执行危险操纵,更改权限等等。
刷新令牌轮换机制
登录后,谷歌会给我们一个【访问令牌】 和 【刷新令牌】。
访问令牌 放在内存里,寿命往往只有几分钟。刷新令牌是以 httpOnly cookie 形式储存(主要是用于辅助刷新【访问令牌】,而不是 API 请求)。
这样很安全。
首先,每隔 10 分钟刷新一遍内存里的 session ,让应用更快了。毕竟在内存里。就算坏人拿到了,也就 10 分钟有效,造成的坏事有限。
另外更好的一点是,就算坏人拿到了 Cookie 里的刷新令牌,但一旦坏人刷新,得到了一个 【访问令牌】,那么谷歌那边就发觉到了,你还在用老的,但坏人却用的新的,于是知道被盗号了,于是强制冲洗登录。
这便是【刷新令牌轮换机制】机制,Auth0、Okta 都是这样。检测到重复使用。服务器销毁链,并以加载页面的罪名将你的用户登出。目前最安全方案。