跳转到内容
Guide

API 密钥、JWT 与 OAuth2:为您的 API 选择正确的身份验证

| 8 min read

比较 API 密钥、JWT 和 OAuth2 的 7 个标准。 通过有效的curl示例了解哪一个适合服务器到服务器调用、用户会话和第三方访问。

Lock and key on a circuit board representing API security
Photo by FLY:D on Unsplash

您正在构建一个 API。 端点有效。 数据流动。 现在你需要回答一个 发货前的问题:来电者如何证明他们是谁?

API 身份验证主要采用三种方法:API 密钥、JWT 和 OAuth2。 每个解决一个 不同的问题。 选择错误的一个,您要么会过度设计一个简单的集成 或者在复杂的安全漏洞中留下安全漏洞。

本指南比较了七个标准中的所有三个标准,并提供了工作代码示例、决策 表,并根据您的 API 用例提出明确的建议。

API密钥认证:直接方法

API 密钥是一个长随机字符串,用于识别调用者并对其进行授权。 客户端发送 对于每个请求,服务器都会查找它,如果它与有效密钥匹配,则该请求 通过。

以下是使用 botoi API 进行 API 密钥调用的方式:

# API key in a custom header
curl -s -X POST https://api.botoi.com/v1/dns/lookup \\
  -H "Content-Type: application/json" \\
  -H "x-api-key: your_api_key_here" \\
  -d '{"domain": "example.com", "type": "A"}'

回复:

{
  "success": true,
  "data": {
    "domain": "example.com",
    "type": "A",
    "records": [
      { "type": "A", "value": "93.184.216.34", "ttl": 86400 }
    ]
  }
}

x-api-key 标头携带凭证。 无需协商令牌,无需重定向, 没有授权服务器。 一个标头、一项查找、一项响应。

当 API 密钥获胜时

  • 服务器到服务器的调用。 你的后端调用另一个后端。 没有用户是 参与。 cron 作业查询 IP 地理定位 API。 CI 管道运行 DNS 检查。 来电者 始终是值得信赖的服务器。
  • 实用程序 API。 执行无状态操作的 API(哈希、编码、 验证、查找),其中每个请求都是独立的。 botoi 使用 API 密钥的原因如下: 150 多个端点,全部无状态,全部服务器到服务器。
  • 快速集成。 开发人员复制密钥,添加一个标头,然后开始 打电话。 没有 OAuth 舞蹈,没有令牌刷新逻辑,没有要配置的 JWKS 端点。

这是 Node.js 中的相同调用:

const response = await fetch("https://api.botoi.com/v1/hash", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "x-api-key": process.env.BOTOI_API_KEY,
  },
  body: JSON.stringify({ text: "hello world", algorithm: "sha256" }),
});

const result = await response.json();
// result.data.hash = "b94d27b9934d3e08..."

API 密钥的不足之处

  • 没有用户身份。 API 密钥标识帐户,而不是用户。 如果 三个开发人员共享一把密钥,您无法分辨谁提出了哪个请求。
  • 撤销需要往返。 撤销密钥意味着更新服务器的密钥 密钥存储。 在缓存刷新之前,旧密钥仍然有效。
  • 没有委派访问权限。 您不能为第三方应用程序提供有限的、临时的 仅使用 API 密钥即可访问用户的资源。

JWT 身份验证:无状态用户会话

JSON Web 令牌 (JWT) 是一个经过签名的 JSON 对象,其中包含有关调用者的声明。 授权 服务器在登录时创建它; 客户端在每次请求时发送它; 资源服务器 验证签名而无需再次调用身份验证服务器。

// Header
{
  "alg": "RS256",
  "typ": "JWT"
}

// Payload
{
  "sub": "user_12345",
  "email": "dev@example.com",
  "role": "admin",
  "iat": 1775000000,
  "exp": 1775000900   // 15 minutes
}

// Signature
RSASHA256(
  base64UrlEncode(header) + "." + base64UrlEncode(payload),
  privateKey
)

服务器使用公钥验证签名。 如果签名签出并且 exp 尚未通过,请求已获授权。 无需数据库查找。

当 JWT 获胜时

  • 具有高流量的面向用户的 API。 移动应用程序会在每个 请求。 API网关在本地验证签名而不是查询会话存储 每次通话时。 在每秒 10,000 个请求的情况下,您跳过的数据库调用很重要。
  • 微服务架构。 服务 A 使用 JWT 调用服务 B。 服务B 在本地验证它并从声明中提取用户的 ID 和角色。 没有共享会话 服务之间的数据库。
  • 短暂的授权。 用于文件上传的 15 分钟令牌。 5分钟 用于付款确认的令牌。 过期时间已融入到代币本身中。

这是验证 JWT 的 Express 中间件:

import jwt from "jsonwebtoken";

function authMiddleware(req, res, next) {
  const token = req.headers.authorization?.replace("Bearer ", "");
  if (!token) return res.status(401).json({ error: "Missing token" });

  try {
    const decoded = jwt.verify(token, process.env.JWT_PUBLIC_KEY, {
      algorithms: ["RS256"],
    });
    req.user = decoded;
    next();
  } catch (err) {
    return res.status(401).json({ error: "Invalid or expired token" });
  }
}

JWT 的不足之处

  • 令牌撤销很难。 JWT 在过期之前一直有效。 如果用户登录 或者您需要撤销访问权限,您需要一个服务器端阻止列表,它可以带回 您试图避免的数据库调用。
  • 有效负载大小。 每个声明都会增加字节。 具有用户角色、权限的 JWT, 元数据可达1-2KB。 每个标头中每个请求的大小为 1-2 KB。
  • 密钥轮换复杂性。 当您轮换签名密钥时,旧令牌将被签名 之前的密钥需要保持有效直到过期。 这意味着服务多个 通过 JWKS 端点的公钥并处理 kid 标头声明。

OAuth2:第三方的委托访问

OAuth2 是一个授权框架,而不是一个身份验证协议。 它允许用户授予 第三方应用程序限制对其在其他服务上的资源的访问,而不共享 他们的密码。

经典示例:用户授权项目管理工具读取其 GitHub 存储库。 用户登录 GitHub,批准特定范围,然后该工具会收到一个 访问令牌的范围仅限于这些权限。

# Step 1: Redirect user to authorization server
GET https://auth.example.com/authorize?
  response_type=code&
  client_id=your_app_id&
  redirect_uri=https://yourapp.com/callback&
  scope=read:repos+write:issues&
  state=random_csrf_token

# Step 2: Exchange authorization code for tokens
POST https://auth.example.com/token
  grant_type=authorization_code&
  code=AUTH_CODE_FROM_CALLBACK&
  client_id=your_app_id&
  client_secret=your_app_secret&
  redirect_uri=https://yourapp.com/callback

# Step 3: Call the API with the access token
curl -H "Authorization: Bearer eyJhbGciOiJSUzI1NiJ9..." \\
  https://api.example.com/v1/repos

当 OAuth2 获胜时

  • 第三方集成。 您运营一个平台并需要外部开发人员 构建可访问用户数据的应用程序。 OAuth2 使用户能够控制每个应用程序的内容 可以访问。
  • 细粒度的范围。 read:repos 但不是 delete:reposwrite:issues 但不是 admin:org。 OAuth2 范围允许用户批准特定权限。
  • “使用 X 登录”流程。 当您的应用程序使用 Google、GitHub 或 Microsoft 进行 登录时,您正在使用 OAuth2(通常在顶部使用 OpenID Connect)来获取身份令牌。

OAuth2 的不足之处

  • 复杂。 OAuth2有四种授权类型,刷新令牌,授权 服务器、重定向 URI、PKCE 和令牌自省端点。 对于实用程序 API 没有用户上下文,这是开销,没有任何好处。
  • 整合摩擦。 想要调用您的哈希端点的开发人员 不想注册 OAuth 应用程序、设置重定向 URI 和交换授权 代码。 他们想要一个键和一个curl 命令。
  • 代币管理负担。 访问令牌过期。 刷新令牌轮换。 客户端需要 401 响应的重试逻辑。 对于简单的服务器到服务器调用,这是 不必要的机械。

对照表

标准 API密钥 智威汤逊 OAuth2
积分时间 分钟 时间
用户身份 账户级别 用户级别(声明) 用户级别(范围)
无状态验证 否(服务器查找) 是(检查签名) 取决于令牌格式
撤销速度 立即(删除键) 延迟(直至到期) 立即(撤销令牌)
授权访问 是的
第三方支持 贫穷的 好的 出色的
适合浏览器 否(关键曝光) 是(短暂) 是(授权码+PKCE)

决策框架:根据您的用例进行选择

不要再问“哪个最安全?” 如果正确使用,这三者都是安全的。 右边的 问题:“谁在调用我的 API,为什么?”

  • 服务器调用服务器,不涉及用户: API 密钥。 你的后端调用 用于 DNS 查找、散列或数据验证的实用 API。 一键一标头,完成。
  • 您自己的应用程序对您自己的用户进行身份验证: 智威汤逊。 您的移动应用程序或 SPA 代表登录用户发送请求。 登录时签署短暂的 JWT,并验证它 在没有会话存储的每个请求上。
  • 第三方应用程序访问用户数据: OAuth2。 外部开发人员构建 与您的平台集成。 用户通过范围控制每个应用程序可以访问的内容 同意屏幕。

许多生产系统结合了这些方法。 GitHub 对第三方应用程序使用 OAuth2 服务器端脚本的 API 密钥(个人访问令牌)。 Stripe 使用 API 密钥 市场平台的直接集成和 OAuth2 (Stripe Connect)。

使用 Unkey 大规模管理 API 密钥

API 密钥听起来很简单,但您需要对它们进行哈希处理、实施速率限制、设置过期时间 日期、跟踪每个密钥的使用情况,并在不停机的情况下轮换它们。 构建所有这一切 从头开始需要几周的时间。

解锁 是一个开放的 源 API 密钥管理服务,用于处理创建、验证、速率限制和 分析。 botoi 使用 Unkey 管理其 150 多个端点上的所有 API 密钥。

创建具有内置速率限制的范围键:

import { Unkey } from "@unkey/api";

const unkey = new Unkey({ rootKey: process.env.UNKEY_ROOT_KEY });

// Create a scoped API key with built-in rate limiting
const key = await unkey.keys.create({
  apiId: "api_your_api_id",
  prefix: "botoi",
  meta: { userId: "user_12345", plan: "pro" },
  expires: Date.now() + 90 * 24 * 60 * 60 * 1000, // 90 days
  ratelimit: {
    type: "fast",
    limit: 100,
    refillRate: 10,
    refillInterval: 1000,
  },
});

// key.result.key = "botoi_3xK9m2..."

在您的中间件中验证它:

import { verifyKey } from "@unkey/api";

async function authMiddleware(req, res, next) {
  const apiKey = req.headers["x-api-key"];
  if (!apiKey) return res.status(401).json({ error: "Missing API key" });

  const result = await verifyKey(apiKey);

  if (!result.result.valid) {
    return res.status(result.result.code === "RATE_LIMITED" ? 429 : 403)
      .json({ error: result.result.code });
  }

  req.keyMeta = result.result.meta; // { userId, plan }
  next();
}

Unkey 存储散列的密钥(从不以纯文本形式),在边缘强制执行速率限制,并给出 您可以看到一个分析仪表板,显示每个键的使用情况。 当密钥需要轮换时,创建一个新密钥 并为旧的设置一个过期时间。 无需停机,无需更改代码。

每种方法的安全检查表

API 密钥

  • 仅通过 HTTPS 发送。 切勿在 URL 或查询字符串中嵌入键。
  • 将哈希值存储在服务器上。 切勿记录原始密钥。
  • 特定权限的范围键(只读、写入、管理)。
  • 设置到期日期。 每 90 天强制轮换一次。
  • 使用自定义标头 (x-api-key)而不是 Authorization 到 避免浏览器凭证缓存。

JWT

  • 使用非对称签名(RS256 或 ES256)。 切勿将 HS256 与共享密钥一起使用 分布式系统。
  • 保持令牌生命周期较短:访问令牌为 5-15 分钟。
  • 证实 iss, aud, 和 exp 对每一个索赔 请求。
  • 通过 JWKS 端点发布公钥。 按计划轮换签名密钥。
  • 切勿在有效负载中存储敏感数据。 JWT 是编码的,而不是加密的。

OAuth2

  • 对所有客户端(包括 SPA 和移动客户端)使用带有 PKCE 的授权代码流程 应用程序。
  • 切勿使用隐式流。 它在 OAuth 2.1 中被弃用是有充分理由的。
  • 仅将客户端机密存储在服务器上。 切勿将它们发送到移动应用程序或前端 代码。
  • 证实 state 参数以防止对回调 URL 的 CSRF 攻击。
  • 使用带有刷新令牌轮换的短期访问令牌。

要点

  • API 密钥 是服务器到服务器实用程序 API 的正确选择。 他们是 集成速度快,易于管理,并且在不需要用户身份时就足够了。 博托伊 通过 Unkey 进行管理,在 150 多个端点上使用它们。
  • JWT 是无状态用户会话的正确选择。 他们消除 大规模会话存储查找,但令牌撤销需要额外的基础设施。
  • OAuth2 当第三方应用程序需要范围访问时,这是正确的选择 用户资源。 它提供的安全模型证明了其复杂性。
  • 根据来电者进行选择,而不是炒作。 OAuth2 实用程序 API 设计过度。 一个 仅具有 API 密钥的平台 API 无法授予委派访问权限。
  • 当您的产品需要时组合方法。 用于直接集成的 API 密钥、OAuth2 对于市场合作伙伴,JWT 用于登录用户会话。

FAQ

我什么时候应该使用 API 密钥而不是 OAuth2?
当调用者是服务器而不是用户时,请使用 API 密钥。 API 密钥非常适合服务器到服务器集成、CI/CD 管道和实用 API,其中每个请求都来自受信任的后端。 当不涉及最终用户同意或委托访问时,OAuth2 会增加不必要的复杂性。
我可以同时使用 JWT 和 OAuth2 吗?
是的。 OAuth2定义了授权流程; JWT 定义了令牌格式。 许多 OAuth2 提供商都会颁发 JWT 作为访问令牌。 JWT 携带资源服务器验证的声明(用户 ID、范围、过期),而无需在每个请求上调用身份验证服务器。
API 密钥对于生产来说足够安全吗?
当您将 API 密钥视为密码时,它们是安全的。 仅通过 HTTPS 发送它们,将它们散列存储在服务器上,将它们限定为特定权限,设置到期日期,并按计划轮换它们。 Unkey 等服务可以为您处理哈希、速率限制和过期。
如何在 JWT 过期之前撤销它?
JWT 是无状态的,因此撤销需要额外的基础设施。 常见的方法包括对每个请求检查的服务器端阻止列表、与刷新令牌配对的短期令牌(5-15 分钟)或针对数据库验证的令牌版本声明。 每种方法都增加了服务器端检查,这在一定程度上抵消了无状态的好处。
身份验证和授权有什么区别?
身份验证验证身份:“你是谁?” 授权决定访问:“你能做什么?” API 密钥通常将两者合并为一个凭证。 OAuth2 在设计上将它们分开,让用户(身份验证)向第三方应用程序授予有限的权限(授权),而无需共享其密码。

开始使用 botoi 构建

150+ 个 API 端点,涵盖查询、文本处理、图片生成和开发者工具。免费套餐,无需信用卡。