开发者必备知识 - HTTP 认证(HTTP Authentication)

92 阅读7分钟

认证(Authentication)是构建 Web 应用或 Web 服务器不可或缺的一环。以下简要介绍各种常见的 HTTP Authentication 方式。

概览 Overview

  • Basic:RFC 2617 (1999) → RFC 7617 (2015)
  • Digest:RFC 2069 (1997) → RFC 2617 (1999) → RFC 7617 (2015)
  • OAuth 1.0(Twitter,2007)
  • OAuth 2.0(2012)
    • Bearer(OAuth 2.0):RFC 6750 (2012)
  • JSON Web Tokens (JWT):RFC 7519 (2015)

参考 MDN - HTTP 认证

行业标准

W3C 在 RFC 1945: HTTP/1.0 (1996) 中定义了 HTTP 架构的认证方式。

服务端 - 认证提示

如果客户端发送了未认证的请求,服务端会返回:

HTTP 401 Unauthorized
WWW-Authenticate: <type> realm="xxx" ...

例如:

WWW-Authenticate: Basic realm="用户可见的作用域"

客户端 - 认证

客户端会在请求头中添加以下信息进行身份验证:

Authorization: <type> <credentials>
  • type:认证方式,例如 Basic、Digest 等
  • credentials:与 type 对应的凭证信息

例如:

Authorization: Basic YWxpY2U6c3VwZXJtYW4=

I. 基础认证(Basic Authentication)

使用用户名和密码组成认证凭证,例如:

  • 用户名:alice,密码:superman
  • 拼接为:alice:superman
  • Base64 编码后:YWxpY2U6c3VwZXJtYW4=
  • HTTP 请求头:
Authorization: Basic YWxpY2U6c3VwZXJtYW4=

顾名思义“基础认证”是最简单、快捷的认证方式,用户甚至无需通过登录页面即可完成认证。

存在的问题
  • 服务端只能验证账号密码,无法控制登录状态的有效期(如会话超时),只能依赖浏览器 Cookie 的过期机制。
  • 攻击者可以解码请求头,直接获取用户名和密码明文。
  • 存在重放攻击(Replay Attack)风险。

因此,后来出现了更复杂的方案:摘要认证(Digest Authentication)。

II. 摘要认证(Digest Authentication)

以下是摘要认证中密钥的生成方式:

HA1 = MD5(用户名:域:密码)
HA2 = MD5(请求方法:摘要URI)
响应值 = MD5(HA1:随机数:HA2)
实际使用流程

服务端返回401 Unauthorized响应:

HTTP/1.0 401 Unauthorized
WWW-Authenticate: Digest realm="testrealm@host.com",
                        qop="auth,auth-int",
                        nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",
                        opaque="5ccc069c403ebaf9f0171e9517f40e41"

随后用户计算出摘要的响应值并发送给服务端:

Authorization: Digest username="Mufasa",
                     realm="testrealm@host.com",
                     nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",
                     uri="/dir/index.html",
                     qop=auth,
                     nc=00000001,
                     cnonce="0a4f113b",
                     response="6629fae49393a05397450978507c4ef1",
                     opaque="5ccc069c403ebaf9f0171e9517f40e41"

参考:维基百科 - 摘要认证

优点
  • 可以加入 URI 等信息,实现文件级别的访问控制。
  • 引入了随机数(Nonce),有效防止选择明文攻击和重放攻击。
存在的问题
  • 需要先从服务端获取随机数(nonce),因此需要多一次请求。
  • 服务端仍然无法主动控制用户的会话状态(如强制登出)。

III. OAuth 1.0

基础认证(Basic)和摘要认证(Digest)都直接使用用户的账号密码作为凭证进行身份验证。但随着 Google、Facebook、Twitter 等平台的普及,网站希望:

  • 借助这些大平台,方便用户登录自己的网站(即“第三方登录”)
  • 代表用户在这些平台上执行某些操作(如发布动态)

因此 OAuth 架构应运而生。与 Basic 和 Digest 相比,OAuth 的核心区别在于:将用于身份验证的用户凭证(如用户名、密码)与实际执行操作的访问令牌(Access Token)分离。这一设计理念使得“使用 Facebook 登录”等应用成为可能,并且是安全的。

认证流程

OAuth 流程涉及三方:

  • 用户(User)
  • 客户端(Consumer)
    • 通常是你要使用的网站或 App
  • 服务提供方(Server / Service Provider)
    • 如 Facebook、Twitter 等平台

OAuth 使用俗称的“三方授权”(3-legged OAuth),分为三个阶段。以下以 Twitter 为例说明:

  1. 前提准备
    • 网站已在 Twitter 注册为客户端(Consumer)。
  2. 获取临时凭证
    • 网站向 Twitter 申请一个未认证的请求令牌(Request Token)。
  3. 用户授权
    • 将用户重定向到 Twitter 的登录页面。
    • 用户登录并授权该请求令牌。
    • 授权完成后,用户被重定向回原网站。
  4. 换取访问令牌
    • 网站将请求令牌发送给 Twitter。
    • 成功换取一个长期有效的访问令牌(Access Token)。

流程图参考

UmvA7.png


IV. OAuth 2.0

OAuth 2.0 在 1.0 的基础上进行了简化,优化了流程,并更好地支持移动应用(App)和单页应用(SPA)的开发。

认证流程

“三方授权”被简化为“两方授权”,客户端不再需要预先获取请求令牌。

流程图参考

Xn4c0.png

OAuth 2.0 官方流程图

Bearer Token(承载令牌)

OAuth 2.0 使用 Bearer Token 作为认证后的访问令牌,极大简化了请求头的结构。

OAuth 1.0 示例:

Authorization: OAuth oauth_consumer_key="cChZNFj6T5R0TigYB9yd1w",
                     oauth_nonce="a9900fe68e2573b27a37f10fbad6a755",
                     oauth_signature="39cipBtIOHEEnybAR4sATQTpl2I%3D",
                     oauth_signature_method="HMAC-SHA1",
                     oauth_timestamp="1318467427",
                     oauth_token="NPcudxy0yU5T3tBzho7iCotZ3cnetKwcTIRlX0iwRl0",
                     oauth_version="1.0"

是不是很复杂?

OAuth 2.0 示例:

Authorization: Bearer cn389ncoiwuencr

简洁清爽,一目了然。与 Basic 和 Digest 不同,Bearer Token 并没有规定具体的生成算法,只要服务端能够验证即可。

参考:OAuth 2.0 入门介绍


V. JSON Web Tokens (JWT)

基础认证和摘要认证的令牌由用户名、密码等信息构成,JWT 的原理类似,但有以下关键区别:

  • JWT 可以携带更多(任意)的用户信息(声明)。
  • 采用哈希(Hash)和数字签名(Signature)进行验证。
  • 加入了服务端的密钥(Secret),由服务端签发令牌,客户端无法伪造。
令牌组成方式

JWT 由三部分组成,用 . 连接:

  1. Header(头部):描述 JWT 的元数据(如签名算法)。
  2. Payload(载荷):存放用户信息(如用户 ID、角色等)。
  3. Signature(签名):对前两部分进行签名,确保完整性。

生成过程:

签名数据 = base64UrlEncode(头部) + "." + base64UrlEncode(载荷)
签名 = HMACSHA256(签名数据, 密钥)
最终令牌 = 签名数据 + "." + base64UrlEncode(签名)

参考:JWT 官方文档

示例

Header:

{
  "alg": "HS256",
  "typ": "JWT"
}

Payload:

{
  "sub": "1234567890",
  "name": "John Doe",
  "admin": true,
  "iat": 1516239022
}

密钥(Secret):

KKF2QT4fwpMeJf36POk6yJV_adQssw5c

生成的 JWT:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.tImCzvIkqaNmGB5mMAG1DZRnZO56sjoYO5nU2YUdRK4

注意:Bearer Token 没有强制规定算法,JWT 正是一种常见的 Bearer Token 实现,通常也以 Bearer 作为前缀。

认证流程

流程非常简单:用户登录后,服务端返回一个 JWT。之后每次请求,客户端只需在 Authorization 头中携带此令牌即可。服务端收到后,只需用自己的密钥重新计算签名并验证,即可确认令牌的有效性。这种方式非常适合无状态的分布式系统和移动应用。

17.png

潜在问题:重放攻击(Replay Attack)

JWT 本身不包含随机数(nonce),攻击者可以截获并重复发送同一个请求,服务端仍会认为是合法请求。

解决方案:
  • 使用 JWT ID(JTI)配合黑名单机制(但这破坏了无状态的初衷)。
  • 在 JWT 外部自行添加随机数或时间戳机制。

推荐阅读:Auth0 关于 JWT 攻击与防范的深度文章


其他基于令牌的认证方式

JWT 是一种基于令牌的认证(Token-based Authentication),类似的方案还有很多,例如 Django REST Framework 的 TokenAuthentication,其基本流程都是:用户登录 → 服务端返回访问令牌 → 后续请求携带令牌。


总结

本文简要介绍了以下几种 HTTP 认证方式:

  • Basic
  • Digest
  • OAuth 1.0
  • OAuth 2.0
  • JWT

整个发展历程可以总结为:

从依赖浏览器功能和 RFC 标准,逐步演进为更简单、更符合“关注点分离”原则的方式(一次请求 + 一个令牌),更好地支持了移动端应用和单页应用(SPA)的发展。