认证(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 为例说明:
- 前提准备
- 网站已在 Twitter 注册为客户端(Consumer)。
- 获取临时凭证
- 网站向 Twitter 申请一个未认证的请求令牌(Request Token)。
- 用户授权
- 将用户重定向到 Twitter 的登录页面。
- 用户登录并授权该请求令牌。
- 授权完成后,用户被重定向回原网站。
- 换取访问令牌
- 网站将请求令牌发送给 Twitter。
- 成功换取一个长期有效的访问令牌(Access Token)。
流程图参考:
IV. OAuth 2.0
OAuth 2.0 在 1.0 的基础上进行了简化,优化了流程,并更好地支持移动应用(App)和单页应用(SPA)的开发。
认证流程
“三方授权”被简化为“两方授权”,客户端不再需要预先获取请求令牌。
流程图参考:
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 并没有规定具体的生成算法,只要服务端能够验证即可。
V. JSON Web Tokens (JWT)
基础认证和摘要认证的令牌由用户名、密码等信息构成,JWT 的原理类似,但有以下关键区别:
- JWT 可以携带更多(任意)的用户信息(声明)。
- 采用哈希(Hash)和数字签名(Signature)进行验证。
- 加入了服务端的密钥(Secret),由服务端签发令牌,客户端无法伪造。
令牌组成方式
JWT 由三部分组成,用 . 连接:
- Header(头部):描述 JWT 的元数据(如签名算法)。
- Payload(载荷):存放用户信息(如用户 ID、角色等)。
- 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 头中携带此令牌即可。服务端收到后,只需用自己的密钥重新计算签名并验证,即可确认令牌的有效性。这种方式非常适合无状态的分布式系统和移动应用。
潜在问题:重放攻击(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)的发展。