直接回答:前端鉴权要解决的是"HTTP 本身不记人"这件事。传统服务端渲染的站点用 Cookie + Session 最省事,前后端分离并且要跨域调 API 时用 Token / JWT 更顺手;两者的差别不在"谁更安全",而在于状态放在服务端还是客户端——放服务端能随时踢人,放客户端要靠过期时间和黑名单兜底。

一切要从"HTTP 是无状态的"说起

HTTP 请求之间互相不认识,服务器处理完一个请求就把你忘了。所以"登录过"这件事必须由外部携带信息来证明,无非两种办法:

  • 服务器给你一个编号,真正的状态存在服务端(Session);
  • 服务器给你一张自证的凭据,状态内容就写在凭据里(Token / JWT)。

至于凭据放在哪、怎么传,浏览器给的标准答案就是 Cookie。

Cookie:浏览器最原始的凭证容器

Cookie 是服务器通过 Set-Cookie 写进浏览器的一小段键值对,之后每次请求浏览器都会自动带上。它的几个关键属性值得记牢:

属性 作用
Domain / Path 哪些域名和路径下会带上这个 Cookie
Expires / Max-Age 会话 Cookie 还是持久 Cookie
HttpOnly 禁止 JS 读取,防 XSS 偷凭据
Secure 只在 HTTPS 下发送
SameSite Lax / Strict / None,直接决定能不能防 CSRF

两个常被忽略的点:

  • Cookie 在跨域请求里默认不发,要用 withCredentials(前端)配合 Access-Control-Allow-Credentials 和不能是 * 的 Access-Control-Allow-Origin(后端);
  • SameSite=None 必须同时带 Secure,否则浏览器直接丢弃,这是"本地开发好好的、上线就登录不上"的经典原因。

Session:状态留在服务端

流程很短:登录成功后服务端生成一个 sessionId(通常是个随机字符串),把用户信息存进服务端的内存、Redis 或数据库,再把 sessionId 写进 Cookie。

优点:

  • 用户信息在服务端,改权限、强制下线、查看在线用户都容易;
  • 浏览器自动携带,前端几乎不用写代码。

代价:

  • 服务端要有存储,多实例部署得做共享(所以常见组合是"Session 存 Redis");
  • 移动端、第三方调用不方便——不是浏览器就没有 Cookie;
  • 跨域场景要额外处理凭证和 CORS。

Token:把凭据给客户端保管

Token 的思路是把凭据交给客户端,请求时放在 Authorization 头里:

1
Authorization: Bearer <token>

好处是不再依赖 Cookie,所以 App、小程序、第三方 API 调用都能用同一套;坏处是浏览器不会自动带,也没有自动过期,全得自己管。

JWT:Token 的一种具体格式

JWT 不是"另一种鉴权方案",它只是把 Token 定义成三段式、能被服务端验签的字符串:

1
header.payload.signature
  • header:算法和类型;
  • payload:真正的内容(用户 id、过期时间 exp、签发者 iss 等);
  • signature:前两段加上密钥算出来的签名。

最需要记住的三句话:

  1. payload 只是 Base64 编码,不是加密。 任何人都能解开看到内容,所以里面不能放敏感信息;
  2. 签名保证的是"没被篡改",不是"不可解读"。 服务端用同一把密钥重新算一遍签名就能验证真伪;
  3. JWT 一旦签发,在过期前一直有效。 想立刻作废只能靠额外的黑名单或者换密钥,所以 exp 不要设太短也不好设太长——通常配一个有效期短的 access token 加一个有效期长的 refresh token。

refresh token:怎么让用户不天天登录

典型做法是发两个凭据:

  • access token:有效期几分钟到几小时,请求业务接口用;
  • refresh token:有效期几天到几个月,只能用来换新的 access token。

access token 过期时,前端拿 refresh token 去换一对新的。这样即使 access token 被截获,窗口期也很短;refresh token 可以存在服务端并支持主动失效,等于把"踢人"的能力补回来了。

前端实现上要注意:并发请求同时触发续期时要做串行处理,否则会拿着同一个 refresh token 换好几次,有些实现会把旧 token 直接作废,导致用户被挤下线。

单点登录:分两种难度

如果站点是 a.example.com 和 b.example.com,把 Cookie 的 Domain 设成 .example.com,两个站点就能共享同一个会话 Cookie,登录态天然打通。这也是最简单的"看起来能单点登录"的做法。

一级域名不同:需要独立的认证中心

域名都不同,Cookie 共享不了,只能有一个独立的认证服务,主流有三条路:

  • CAS:未登录访问业务系统 → 重定向到 CAS → 登录后在地址栏带一个 ticket 跳回业务系统 → 业务系统拿 ticket 到 CAS 后台换取用户信息;
  • OAuth 2.0:本质是授权协议,常用于第三方登录,流程是授权码换 access token,再拿 token 取用户信息;
  • OIDC:在 OAuth 2.0 之上补了身份层,返回 id_token(就是一个 JWT),现在新项目大多直接选它。

不管哪种,业务系统都要有一份本地会话,否则每个请求都去认证中心校验一次,认证中心会成为瓶颈和单点故障。

怎么选

场景 建议
传统的服务端渲染站点 Cookie + Session(存 Redis),最省事
前后端分离、同域部署 Cookie + Session 也可以,不必为了"潮"上 JWT
前后端分离、跨域,或者要给 App / 第三方调用 Token + JWT,配 refresh token
多个系统要做统一登录 独立认证中心 + OIDC,业务系统保留本地会话

最后三条安全底线,跟选哪种无关:

  1. 凭据放 Cookie 就加 HttpOnly + Secure + 合适的 SameSite,能挡掉大半 XSS 窃取和 CSRF;
  2. 用 localStorage 存 token 时,必须自己防 XSS,因为 JS 能读到的东西 XSS 也能读到;
  3. 鉴权一律在服务端做,前端路由守卫只是体验优化,不是安全措施。

本文整理自我自己早先收集的一份鉴权资料,按自己的理解重新组织并补充了实践中的注意点,文字由 AI 协助改写后经我复核。细节以各协议的正式规范和所用框架的文档为准。

这篇笔记整理自我自己的实践记录,如果做法有出入,或者你踩过别的坑,欢迎到留言板一起聊聊。

站内搜索

没有找到内容!