每个API请求都需要证明是谁在发起该请求。你选择的方法将影响你的安全态势、开发者体验以及多年来的运营开销。基本身份验证、API密钥、承载令牌和OAuth每种方法解决的问题不同——使用错误的方法会带来难以解决的债务。以下是对每种方法的清晰说明,包含可复制粘贴的代码和决策表,帮助你为具体场景选择合适的方法。
HTTP基本身份验证
基本身份验证在每次请求中都会发送凭据。客户端将用户名和密码组合成 username:password,然后进行Base64编码,并将其放入 Authorization 头中:
Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=
该Base64字符串是 未加密的。任何截获请求的人都可以在几秒钟内解码它。基本身份验证仅在HTTPS下是安全的,即使如此,凭据也会在每次请求中传输,并且除非你主动清除,否则会出现在服务器日志中。
要生成正确的头值而无需手动编码凭据,请使用 IO Tools 基本身份验证生成器.
# curl with Basic Auth
curl -u username:password https://api.example.com/data
# Or with the explicit header
curl -H "Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=" https://api.example.com/data
// fetch with Basic Auth
const credentials = btoa('username:password');
fetch('https://api.example.com/data', {
headers: { Authorization: `Basic ${credentials}` }
});
适用情况: 内部工具、开发环境以及你控制两端的简单服务器间集成。绝不要用于面向公众的API或用户身份验证。
API密钥
API密钥是一个静态令牌——一个长随机字符串,与特定应用程序或调用者相关联。客户端将其发送在头中,通常在 X-API-Key 或通过 Authorization 头,使用自定义方案:
# curl with API key
curl -H "X-API-Key: sk_live_abc123xyz" https://api.example.com/data
# Or with Authorization header
curl -H "Authorization: ApiKey sk_live_abc123xyz" https://api.example.com/data
// fetch with API key
fetch('https://api.example.com/data', {
headers: { 'X-API-Key': 'sk_live_abc123xyz' }
});
API密钥易于实现,并且一旦被泄露可以立即撤销。缺点是:它们是无状态的,且没有内置过期时间。一旦泄露,密钥将持续有效,直到你手动撤销。没有签名来验证,也没有嵌入的范围——只是在数据库中查找的一个字符串。
何时使用: 第三方集成、开发者API产品以及公开API访问,你希望对每个客户端实施速率限制并立即撤销,而无需OAuth的开销。
承载令牌(JWT)
承载令牌——最常见的是JWT(JSON Web Token)——由认证服务器签发,并发送在 Authorization 头中,使用 Bearer 方案:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
JWT携带一个签名的有效载荷,包含声明:用户是谁、他们拥有哪些权限以及令牌何时过期。服务器通过验证签名与共享密钥或公钥进行比对来验证令牌——无需数据库查询。这种无状态验证是分布式系统和微服务架构中的主要优势。
存在的权衡是真实的:JWT较大(每个请求几百字节),且在过期前无法被无效化,除非有额外的基础设施,如令牌黑名单。实现错误——弱签名密钥、缺少过期检查、算法混淆攻击——已导致生产系统中出现严重安全漏洞。
# curl with Bearer token
curl -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
https://api.example.com/data
// fetch with Bearer token
fetch('https://api.example.com/data', {
headers: { Authorization: `Bearer ${token}` }
});
何时使用: 面向用户的API、需要向下游传递身份的微服务,以及任何场景中短生命周期凭据带有嵌入声明,从而减少对服务器端会话状态的需求。
OAuth 2.0:访问令牌和刷新令牌
OAuth 2.0 不是一种令牌格式,而是一种委托协议。当你的应用程序需要代表用户访问另一个服务的资源时,OAuth 2.0 处理用户授权和令牌交换。
流程简述:用户批准访问,授权服务器签发一个短期的 访问令牌 和一个长期的 刷新令牌,你的应用程序使用访问令牌进行API调用,当访问令牌过期时,刷新令牌可换取新的令牌,而无需再次提示用户。
# Step 1: Exchange credentials for a token (client credentials flow)
curl -X POST https://auth.example.com/token \
-d "grant_type=client_credentials" \
-d "client_id=myapp" \
-d "client_secret=mysecret"
# Step 2: Use the access token
curl -H "Authorization: Bearer ACCESS_TOKEN" https://api.example.com/data
# Step 3: Refresh when expired
curl -X POST https://auth.example.com/token \
-d "grant_type=refresh_token" \
-d "refresh_token=REFRESH_TOKEN" \
-d "client_id=myapp"
访问令牌通常为JWT。刷新令牌是无意义的字符串,存储在服务器端——绝不要将其暴露给浏览器端代码。
何时需要: 社交登录、第三方数据访问、任何“使用X登录”集成,或任何需要人类用户同意应用程序可以代表其做什么的场景。
适用于所有方法的安全规则
无论你选择哪种身份验证方法,以下规则均适用,无例外。
- 所有场景都必须使用HTTPS。 凭据或令牌在明文HTTP中传输时,一旦有人能查看数据包,就已泄露。没有例外。
- 绝不将密钥存储在代码中。 使用环境变量或密钥管理器。不要在版本控制文件中存储凭据——包括被
.gitignore排除的文件,因为这些排除在实际中不可靠。 - 定期轮换,并在怀疑时立即轮换。 API密钥应支持无中断轮换。JWT签名密钥应支持版本化,以便在不同时效性所有活动会话的情况下进行轮换。
- 最短有效时间即可。 访问令牌:几分钟到几小时。API密钥:在任何人员变更时轮换。基本身份验证凭据:视为特权信息并主动轮换。
- 审计谁拥有什么。 维护已颁发凭据的注册表。当出现问题时,你需要知道具体颁发了什么、给谁、何时颁发。
决策指南:哪种方法适用于哪种场景
| 方法 | 无状态 | 可撤销 | 复杂 | 较小——只是一个 ID |
|---|---|---|---|---|
| Basic Auth | 是的 | 仅通过更改凭据 | 非常低 | 内部工具、开发环境 |
| API密钥 | 是的 | 是,立即可撤销 | 低的 | 第三方集成、开发者API |
| 承载(JWT) | 是的 | 仅通过令牌黑名单 | 中等的 | 面向用户的API、微服务 |
| OAuth 2.0 | 因情况而异 | 是的 | 高的 | 用户委托、第三方认证 |
内部API、服务器间通信、无用户: API密钥。易于实现,可立即撤销,易于审计。如果你已经在运行使用JWT的微服务,可以使用短生命周期的服务账户令牌代替。
面向公众的API,有外部开发者消费者: API密钥,带有按密钥的速率限制和自助管理门户。如果消费者需要代表其用户请求访问特定资源,则添加OAuth范围。
你产品中的用户身份验证: 使用短有效期的承载令牌(JWT),并配合刷新令牌轮换。在凭据验证后签发令牌,保持其短生命周期,并避免在存在XSS风险的应用中持久存储它们。 localStorage 访问第三方服务以代表你的用户:
OAuth 2.0 授权码流程。不要简化此流程。用户委托模型存在是因为它是大规模处理第三方授权的最安全方式。 正确的选择通常取决于两个问题:调用者是谁,以及是否需要人类用户同意该调用者的行为?如果调用者是机器且不涉及用户授权,则API密钥可以很好地处理大多数情况。需要嵌入声明或无状态跨服务身份时,添加JWT。只有在用户授权是流程的一部分时,才选择OAuth。
基本身份验证与承载令牌:应使用哪种API身份验证方法 2
