深入理解 Cookie 与 Authorization

从购物车到第三方登录,全面梳理认证授权的核心机制

Cookie Authorization OAuth2 第三方登录

目录导航

一、Cookie:浏览器的记忆面包

1 起源:购物车的诞生

早期 Web 基于 HTTP 协议,而 HTTP 是无状态的,服务器无法记住用户上一次做了什么。这导致一个很实际的问题:用户在电商网站把商品加入购物车,刷新页面后购物车就空了。为了解决这个痛点,网景公司的工程师 Lou Montulli 在 1994 年发明了 Cookie。有了它,服务器可以将购物车数据交给浏览器"记住",下次访问时再带回给服务器,从而实现了状态保持。

2 工作机制

Cookie 的本质是服务器委托浏览器存储的一小段数据(通常不超过 4KB)。这个机制分两步:

  • Set-Cookie:当浏览器访问服务器时,服务器在 HTTP 响应头中带上 Set-Cookie: key=value; ...,浏览器收到后会将该数据存储到本地,并明确记录该 Cookie 属于哪个域名
  • 自动回传:之后,只要再次访问同一域名(且满足路径、安全等条件),浏览器就会在请求头中自动附上 Cookie: key=value,将数据无脑发回服务器。

这个过程完全由浏览器自动完成,无需 JavaScript 干预,是 HTTP 状态管理的基石。

3 核心应用场景
  • 会话管理:最经典的用途是保存 sessionId。用户登录后,服务器生成一个唯一的会话标识符,通过 Set-Cookie 传给浏览器。后续请求带上此标识符,服务器就能查到对应的登录用户,维持登录状态。
  • 个性化设置:保存用户的偏好,例如网站语言、主题颜色、页面布局等。即便不登录,也能提供定制体验。
  • 用户追踪:广告联盟或分析工具(如 Google Analytics)通过第三方 Cookie 记录用户浏览行为,实现跨站追踪和精准广告投放。由于隐私问题,第三方 Cookie 正逐渐被浏览器淘汰(如 Safari 的 ITP、Chrome 的 Privacy Sandbox)。
4 安全防护:给 Cookie 加锁

Cookie 如果不加限制,极易成为攻击的跳板,因此诞生了多个重要的属性:

XSS 与 HttpOnly

跨站脚本攻击(XSS)是通过注入恶意脚本窃取用户的 Cookie。如果 Cookie 标记了 HttpOnly,浏览器将禁止 JavaScript(document.cookie)读取该 Cookie,它仅用于 HTTP 请求传输,从而大幅降低 XSS 窃取会话的风险。

XSRF/CSRF 与 SameSite

跨站请求伪造(CSRF/XSRF)是利用用户已登录的身份,在第三方网站向目标站点发送恶意请求(如转账、改密)。因为请求会自动附带目标域名的 Cookie,攻击者能"借用"用户的身份。

传统的防御手段之一是检查 Referer请求头,判断请求来源是否合法,但 Referer 可能被隐藏或篡改。更可靠的方式是结合 CSRF Token(服务器下发随机令牌,提交时必须带上)和 SameSite 属性

  • SameSite=Strict:仅当请求来自同站时才发送 Cookie,防御最强,但可能影响从外部链接打开时的登录状态。
  • SameSite=Lax:允许在顶级导航的 GET 请求中发送 Cookie(如点击链接),阻止在 POST 表单提交、iframe 等场景发送,平衡了安全与体验,已成为现代浏览器的默认行为。
  • SameSite=None:必须同时设置 Secure,允许跨站发送,适用于第三方嵌入式场景。

Secure 属性

标记 Secure的 Cookie 仅在 HTTPS 加密连接中传输,防止中间人嗅探。如今生产环境都应开启 HTTPS 并为敏感 Cookie 加上此标记。

二、Authorization:证明"你是谁"与"你能做什么"

Authorization 头专门用于携带认证凭证,告诉服务器"我是谁"或"我被授权访问什么"。它与 Cookie 最大的区别在于:Authorization 必须由客户端代码主动构造并附加,不会像 Cookie 那样由浏览器自动携带,因而天然对 CSRF 免疫(但更需防范 XSS 窃取令牌)。

1 Basic 认证

最朴素的 HTTP 认证方式,格式为:

Authorization: Basic <base64(username:password)>

客户端将用户名和密码用冒号拼接,进行 Base64 编码后放入请求头。

注意:Base64 只是编码,不是加密!在 HTTP 明文传输下,密码极易泄露。因此 Basic 认证必须在 HTTPS 下使用,否则毫无安全性可言。它通常用于简单 API 调用、内部系统等场景。

2 Bearer Token 与 OAuth2

格式为:

Authorization: Bearer <token>

Bearer 意为"持有者",任何人只要拿到这个令牌就能代表用户。由于它通常不依赖 Cookie,天然防 CSRF,但必须严防令牌泄漏。Bearer Token 最常见的来源就是 OAuth2 协议。

OAuth2 的核心角色

  • 资源拥有者:用户
  • 客户端:第三方应用,如掘金网站
  • 授权服务器:GitHub、微信等平台的认证服务
  • 资源服务器:存放用户数据的服务(常与授权服务器同属一个平台)

第三方登录的本质

掘金使用 GitHub 登录为例。掘金说:"用户,请让 GitHub 告诉我们你的基本信息。" 这个过程其实是用户授权 GitHub 将一部分数据访问权限交给掘金。掘金拿到权限后,从 GitHub 获取用户信息,再在自己的系统中创建或绑定一个账号,最后以该账号完成登录。

这里容易混淆的是:第三方登录的"第三方"是 GitHub,而授权的"第三方"是掘金。从用户视角看,我用 GitHub 账号登录了掘金;从协议视角看,我授权掘金访问我的 GitHub 信息。

完整的 OAuth2 授权码流程(最安全的标准流程)

为什么不能直接把 Token 给浏览器?因为浏览器环境不可控,直接给 Token 容易泄漏。OAuth2 通过授权码(Authorization Code)配合后端密钥来保证安全。

1. 发起授权请求

用户点击"使用 GitHub 登录",掘金前端引导浏览器跳转到 GitHub 授权页面(https://github.com/login/oauth/authorize),携带参数:

  • client_id:GitHub 事先分配给掘金的公开标识。
  • redirect_uri:授权后跳转回来的掘金回调地址。
  • scope:请求的权限范围(如读取用户资料)。

2. 用户同意授权

GitHub 授权页面会展示掘金的图标和应用名称(这个图标和名称是掘金在 GitHub 后台注册应用时填写的,由 GitHub 服务器调取,并非掘金前端传递),并询问用户是否授权。用户点击"同意"。

3. 获取授权码(Authorization Code)

GitHub 将浏览器重定向回掘金指定的 redirect_uri,并在 URL 上附带一个临时授权码 code。这个 code有效期很短(通常几分钟),且只能使用一次。它仅仅代表"用户同意了",但还不是真正访问数据的钥匙。

4. 换取访问令牌(Access Token)

掘金的后端服务器拿到浏览器传回的 code后,立即向 GitHub 的令牌端点(https://github.com/login/oauth/access_token)发送 POST 请求,参数包括:

  • client_id
  • client_secret:GitHub 分配给掘金的严格保密的密钥,绝不会暴露到前端。
  • code:刚才的授权码。

因为 client_secret的存在,GitHub 能确认请求确实来自掘金服务器,而不是某个冒充的客户端。这一步全程在服务器之间进行,且必须使用 HTTPS

5. 颁发令牌

GitHub 验证通过后,向掘金服务器返回 access_token(可能还有 refresh_token)。授权过程至此结束。

6. 获取用户信息并完成登录

掘金服务器拿着 access_token,用 Authorization: Bearer <access_token>请求 GitHub 的用户信息接口(https://api.github.com/user)。拿到 GitHub 用户 ID、昵称等信息后,在自己的数据库中查找或创建一个内部账号(通常会以 GitHub ID 或邮件为关联标识),然后生成自己平台的会话(如通过 Set-Cookie下发 sessionId),最后将登录成功的信息返回给浏览器。整个第三方登录流程完成。

关键安全点:授权码在前端出现,但极其短暂且需配合后端密钥才能换令牌,这使得直接暴露给浏览器或中间人攻击的令牌风险大大降低。

微信登录的特殊之处

微信的 OAuth2 流程在原理上与 GitHub 一致,但有一些产品上的特殊设计:

  • 必须跳转微信客户端:在移动端,会唤起微信 App 打开授权页面;在 PC 端,通常会展示一个二维码,用户用微信扫码后在手机端确认。这种设计将用户确认行为限定在微信自身的安全环境中。
  • 应用图标显示:授权页面上显示的 App 图标和名称,同样是开发者在微信开放平台注册应用时上传的,由微信服务器读取展示,增强了用户对授权方的信任。
  • 后续的 code 换 token、获取用户信息等步骤与标准 OAuth2 无异。
3 Refresh Token:令牌的续期机制

Access Token 一般有效期较短(几小时到几天),一旦过期就需要重新登录,体验很差。Refresh Token 解决了这个问题。

Refresh Token 是与 Access Token 一起颁发的、有效期更长(几周甚至几个月)的凭证。流程如下:

  1. 当客户端发现 Access Token 过期或即将过期时,将 Refresh Token 发送给授权服务器的令牌端点。
  2. 服务器验证 Refresh Token 有效性后,颁发一个新的 Access Token,并可选择性地同时下发新的 Refresh Token(轮换策略),同时使旧的 Refresh Token 失效。
  3. 客户端使用新令牌继续访问资源。

这样,即便 Access Token 意外泄漏,其影响窗口也很短;而 Refresh Token 仅在后端使用,且可被服务器主动吊销,兼顾了安全与体验。

三、总结

  • Cookie是浏览器自动管理的"存储-携带"机制,适用于会话保持,但必须通过 HttpOnlySameSiteSecure等属性防范 XSS 和 CSRF。
  • Authorization是客户端主动维护的凭证携带方式,其中 Bearer Token 是现代 API 认证的主流,天然防 CSRF,但需要保护令牌本身安全。
  • OAuth2 授权码流程通过前后端分工,在安全性和易用性之间取得平衡,成为第三方登录的事实标准。理解其"先发码、后端换令牌"的精髓,就能清晰地把握各种社交登录的底层逻辑。

只有深入理解这两大机制的工作原理与安全模型,才能在设计 Web 应用时做出正确的架构决策,为用户提供既便捷又安全的体验。