基础

  • 身份验证(AuthN):“你是谁?” (身份)。
  • 授权 (AuthZ): “你可以做什么?” (权限)。

📦 关键技术

1. 会话(有状态)

  • 它是什么:
    会话是一种服务器端机制,用于在无状态协议中跨多个 HTTP 请求记住用户。

  • 它是如何工作的:

    1. 用户使用凭据登录。
    2. 服务器验证凭据并创建会话。
    3. 服务器将会话数据(例如,用户 ID、角色、登录状态)存储在 Redis/DB/内存中。
    4. 服务器通过 cookie 向客户端发送 session_id
    5. 对于每个后续请求(例如,/profile),浏览器会自动通过 cookie 将 session_id 发送回服务器。
    6. 服务器验证 session_id、检索会话数据并授权用户。
  • 机制:
    服务器将会话数据存储在Redis/DB/内存中;客户端仅将 session_id 存储在 cookie 中。

  • 用例:
    需要高安全性、服务器端控制和即时会话撤销的 Web 应用程序(例如登录系统、管理面板、仪表板)。

  • 优点:

    • 安全(敏感数据保留在服务器上)
    • 易于使会话失效(注销=删除会话)
    • 传统网络应用程序的简单身份验证流程
  • 缺点:

    • 消耗服务器内存/存储
    • 扩展更困难(需要跨服务器共享会话存储)
    • 不适合无状态 API 和移动优先系统

2.JWT(无状态)

  • 它是什么:
    JWT(JSON Web Token)是一种无状态身份验证机制,其中服务器发出包含用户相关数据的签名令牌,客户端在每次验证请求时发送此令牌。

  • 机制:

    1. 用户登录并将凭据发送到服务器。
    2. 服务器验证凭据并生成 JWT。
    3. JWT 包含编码的用户数据(有效负载),并使用秘密/私钥进行数字签名。
    4. 服务器将 JWT 发送给客户端。
    5. 客户端存储令牌(例如,在内存或本地存储中)。
    6. 在每个后续请求中,客户端都会在 Authorization: Bearer <token> 标头中发送 JWT。
    7. 服务器验证令牌签名和有效期,提取有效负载并授权请求。
  • 用例:
    非常适合需要无状态身份验证和轻松水平扩展的微服务、移动 API、公共 API 和大规模分布式系统。

  • 结构:
    JWT 由三个以点分隔的 Base64 编码部分组成:

  • 标题:
    包含有关令牌的元数据,例如签名算法和令牌类型。

    1
    2
    3
    4
    {
    "alg": "HS256",
    "typ": "JWT"
    }
  • 有效负载:
    包含有关用户和令牌的声明(数据),例如 userIdrole 和过期时间 (exp)。

    1
    2
    3
    4
    5
    {
    "userId": 7,
    "role": "user",
    "exp": 1700000000
    }
  • 签名:
    使用编码标头、编码有效负载和秘密/私钥创建的加密签名。
    它确保令牌没有被篡改。

  • 优点:

  • 无状态(无服务器端会话存储)

  • 易于跨多个服务器扩展

  • 快速身份验证(无需 Redis/DB 查找)

  • 非常适合 API 和微服务

  • 缺点:

  • 发出后不可立即撤销

  • 注销更难实施

  • 令牌大小大于会话 ID

  • 敏感数据不应存储在有效负载中

3.OAuth 2.0 和 OIDC

  • OAuth 2.0: 委托。 (例如,让应用程序发布到您的 Twitter)。
  • OpenID Connect: 身份。 (例如,“使用 Google 登录”)。

饼干

  • 它是什么:
    Cookie 是存储在浏览器中的一小段数据,并随每个 HTTP 请求自动发送到同一域。

  • 为什么存在 cookie:
    Cookie 允许服务器在客户端上存储信息并在后续请求中接收信息,从而解决了 HTTP 的无状态特性。

  • 它是如何工作的:

    1. 服务器在响应中发送 Set-Cookie 标头。
    2. 浏览器存储cookie。
    3. 浏览器自动发送带有未来请求的cookie。
  • 常见用途:

    • 存储会话ID
    • 存储 JWT
    • 用户偏好(主题、语言)
    • 分析和跟踪
  • Cookie 与会话与 JWT:

    概念 目的 数据存在的地方
    饼干 仓储+运输 浏览器
    会议 状态认证 服务器
    智威汤逊 无状态认证 客户
  • 与会话的关系:
    Cookie 通常存储 session_id,而实际会话数据(用户 ID、角色、登录状态)存储在服务器(Redis/DB)上。

  • 与智威汤逊的关系:
    Cookie 可以存储 JWT,其中包含签名的用户数据,并由服务器对每个请求进行验证。

  • 常见误解:

    • Cookie 本身并非不安全;安全性取决于正确的标志。
    • Cookie 和会话不是同一回事。
    • JWT 不取代 cookie;它仅替换服务器端会话。
    • Cookie 不应存储密码等敏感数据。
  • 重要的 Cookie 标志:

    • HttpOnly:阻止 JavaScript 访问 cookie。
    • Secure:仅通过 HTTPS 发送 cookie。
    • SameSite:帮助防止 CSRF 攻击。

了解 Cookie:它们实际上存储什么?

Cookie 存储小型、简单的键值数据,浏览器会根据每个请求自动将其发送到服务器。

黄金法则: cookie 是一个指针,而不是数据库。它应该是轻量级的。


🔍 饼干的解剖

当服务器想要设置 cookie 时,它会发送如下标头:
Set-Cookie: sessionId=abc123; HttpOnly; Secure; SameSite=Lax

浏览器商店:

  • 键: sessionId
  • 值: abc123

🛠 现实世界的例子

1.博客应用程序(基于会话的登录)

  • 登录时(服务器响应): Set-Cookie: sessionId=xyz789
  • /profile (浏览器请求)上: 浏览器自动附加 Cookie: sessionId=xyz789

📌 注意: cookie 不会 存储您的用户名或电子邮件。这些详细信息位于服务器上的会话存储 (Redis/DB) 中。

2. 电子商务应用程序(首选项与购物车)

  • 用户偏好: Set-Cookie: currency=INR
  • 购物车: 通常存储在数据库或服务器会话中。

为什么不是购物车? 购物车数据通常对于 Cookie 大小限制来说太大,并且如果存储在客户端,则不安全。

3.JWT(JSON Web 令牌)

服务器发送一个签名字符串:
Set-Cookie: accessToken=eyJhbGciOiJIUzI1Ni...

JWT 内部(在服务器上解码):

1
2
3
4
5
{
"userId": 7,
"role": "user",
"exp": 1700000000
}

身份验证模型的类型

1) 状态认证

服务器存储用户会话数据并使用随每个请求发送的会话 ID 来识别用户。

2) 无状态身份验证

服务器不存储会话;客户端在每个验证请求上发送签名令牌(如 JWT)。

3) API 密钥认证

客户端通过发送静态 API 密钥进行身份验证,该密钥可识别并授权对应用程序或服务的访问。

4) OAuth 2.0 身份验证

用户通过受信任的第三方提供商进行身份验证,该第三方提供商会颁发令牌来访问资源,而无需共享密码。

1) 状态认证

在状态认证中,服务器维护用户的认证状态。

第 1 步:登录

  • 用户使用用户名和密码登录。
  • 服务器验证凭据。
  • 服务器创建会话并将会话数据(例如 userId、角色、登录状态)存储在内存或数据库(Redis/DB)中。
  • 生成唯一的 session_id

第2步:会议分享

  • 服务器通过 cookie 将 session_id 发送到客户端。

第 3 步:经过身份验证的请求

  • 对于每个请求,浏览器都会通过 cookies 自动发送 session_id
  • 服务器使用 session_id 查找会话数据。
  • 如果会话有效,则授予访问权限。

📌 重要说明

  • 密码永远不会存储在会话中。
  • 会话数据存在于服务器,而不是客户端。

2) 无状态认证

在无状态认证中,服务器不存储任何会话数据。

第 1 步:登录

  • 用户使用用户名和密码登录。
  • 服务器验证凭据。
  • 服务器生成一个签名的 JWT,其中包含用户相关的声明(例如 userId、角色、到期日)。

步骤 2:代币交付

  • 签名的 JWT 发送到客户端(通常通过 cookie 或本地存储)。

第 3 步:授权请求

  • 对于每个受保护的请求,客户端都会发送 JWT
    (通常在 Authorization: Bearer <token> 标头中)。

步骤 4:令牌验证

  • 服务器验证 JWT 签名和有效期。
  • 如果有效,则从令牌中提取用户身份并授予访问权限。

📌 为什么 JWT 是无状态的

  • 服务器不存储用户会话数据
  • 所有必需的身份验证数据都包含在令牌本身内。
  • 服务器仅验证令牌签名 - 不需要数据库查找。

3) API 密钥:“软件密码”

API Key 是用于验证不同软件系统之间的请求的唯一标识符。它主要用于 机器到机器服务到 UI 交互。

需求和解决的问题

在现代开发中,服务器需要与其他服务器通信。 API 密钥解决三个主要问题:

  1. 标识: 它告诉提供商正在呼叫(例如,“这是‘WeatherApp’呼叫”)。
  2. 速率限制: 它通过将单个用户限制为每分钟特定的请求数来防止单个用户使服务器崩溃。
  3. 计费和跟踪: 对于付费服务(如 Google 地图或 OpenAI),密钥会跟踪使用情况,以便提供商知道要向您收取多少费用。

什么时候使用?

  • 第三方集成: 当您的应用需要从 GitHub、Stripe 或 Google Maps 等服务提取数据时。
  • 后端自动化: 当您的服务器需要触发另一台服务器上的操作时(例如,通过 SendGrid 发送电子邮件)。
  • 公共数据获取: 访问实时数据源,例如股票价格或天气更新。

示例工作流程

  1. 获取: 您注册 API(例如 OpenWeather)并收到密钥:xyz-789-api-key
  2. 请求: 您的服务器在标头或 URL 中发送此密钥:
    GET /data?city=London&apikey=xyz-789-api-key
  3. 验证: 平台服务器检查密钥。如果有效,它会授予您的服务器访问数据的权限。

安全说明

API 密钥是长期存在且“静态”的。如果有人窃取了您的密钥,他们可以冒充您的应用程序。 永远不要将 API 密钥提交到公共 GitHub 存储库;始终使用 .env 文件。

4) OAuth(开放式身份验证)

OAuth 的演变:从 1.0 到 2.0

OAuth(开放授权)是委托访问的行业标准。它允许一个服务访问您在另一个服务上的数据,而无需您泄露密码。


OAuth 之前的时代:“密码共享”问题

在 OAuth 之前,如果第 3 方应用程序(例如“联系人导入程序”)想要在 Gmail 上查找您的朋友,它会要求您提供实际的 Gmail 用户名和密码

这方面的问题:

  • 信任: 您必须信任该应用程序不会窃取您的密码。
  • 过度访问: 该应用程序可以完全访问您的电子邮件、设置和驱动器,而不仅仅是您的联系人。
  • 撤销: 要停止该应用程序,您必须更改密码,这会破坏您使用的所有其他应用程序。

OAuth 1.0:解决方案(委派访问)

引入 OAuth 1.0 是为了允许使用数字签名进行“委托访问”。

它解决了什么问题?

它允许用户向第三方应用程序授予对其资源(如照片)的访问权限,而无需共享其凭据。

它是如何工作的(三足流):

  1. 请求令牌: 应用程序向服务器请求临时令牌。
  2. 用户授权: 用户被重定向到服务器以批准请求。
  3. 交换: 应用程序将临时令牌交换为 访问令牌

有何不同:

与简单的 API 密钥或密码共享不同,OAuth 1.0 引入了加密握手。每个请求都需要使用秘密进行复杂的签名计算。


OAuth 1.0 的问题

虽然安全,但 OAuth 1.0 对于开发人员来说是一场噩梦:

  1. 复杂性: 计算每个请求的加密签名非常困难并且容易出错。
  2. 以 Web 为中心: 它不适用于移动应用程序或非浏览器环境。
  3. 性能: 服务器必须在每次发出请求时验证签名。

OAuth 2.0:现代标准

OAuth 2.0 是为了灵活性和易用性而设计的完全重写。

它解决了哪些问题?

  • 开发人员体验: 用简单的 不记名令牌(通常通过 HTTPS)替换了复杂的签名。
  • 灵活性: 引入了针对不同场景(移动应用程序、Web 应用程序、服务器端)的“授予类型”。
  • 可扩展性: 令牌可以有过期时间和“范围”(特定权限)。

它是如何工作的(标准流程):

  1. 授权请求: 用户点击“使用 Google 登录”。
  2. 授权授予: 用户批准。服务器发回授权码
  3. 令牌请求: 应用程序将该代码及其 Secret 发送到服务器。
  4. 访问令牌: 服务器发回 access_token (通常是 refresh_token)。
  5. API访问: 应用程序使用令牌来获取数据。

它与 OAuth 1.0 有何不同:

特色 OAuth 1.0 OAuth 2.0
复杂性 高(加密签名) 低(通过 HTTPS 的承载令牌)
代币 没有过期(通常) 过期(带有刷新令牌)
角色 3 个角色(用户、消费者、服务) 4种角色(资源所有者、客户端、授权服务器、资源服务器)
设备支持 主要是网络 网络、移动、物联网、智能电视

心智模型总结

  • API 密钥: 就像永久的建筑密钥。
  • OAuth 1.0: 就像每次进入房间时都必须执行的复杂的编码握手一样。
  • OAuth 2.0: 就像 酒店钥匙卡。您在前台出示您的 ID(登录),他们会给您一张卡(令牌),该卡只能打开您的房间,并在上午 11:00 到期。

5) OIDC:OpenID Connect(身份层)

虽然 OAuth 2.0 解决了授权(你可以做什么)的问题,但它实际上并没有解决身份验证(你是谁)。 OIDC 的创建就是为了填补这一空白。


问题:“身份差距”

在OIDC之前,开发者尝试使用OAuth 2.0进行登录。然而,OAuth 2.0只给了应用程序一个访问数据的“密钥”(令牌);它不会告诉应用程序有关持有钥匙的人的任何信息。

“代客钥匙”的比喻:

  • OAuth 2.0 就像一把代客钥匙。它允许驾驶员移动您的汽车,但钥匙不会告诉驾驶员您的姓名、地址或员工 ID。
  • OIDC 就像出示您的 驾驶执照。它准确地证明了你是谁。

什么是 OIDC?

OIDC 是一个构建在 OAuth 2.0 协议之上的简单身份层。它允许客户端根据授权服务器执行的身份验证来验证最终用户的身份。

它解决了什么问题?

  1. 标准化登录: 在 OIDC 之前,每家公司(Facebook、Google、Twitter)都有自己的共享用户个人资料的方式。 OIDC 使其成为通用标准。
  2. SSO(单点登录): 它允许您使用一个帐户(例如 Google 或 Microsoft)安全地登录数千个不同的网站。
  3. 用户信息: 它提供了获取用户姓名、电子邮件和个人资料图片的标准方法。

工作原理:ID 令牌

OIDC 使用与 OAuth 2.0 相同的流程,但增加了一项主要内容:ID 令牌

当您通过OIDC登录时,服务器返回:

  1. 访问令牌: (OAuth 2.0) 调用API。
  2. ID 令牌: (OIDC) 包含用户信息的 JWT(JSON Web 令牌)。

ID 令牌内容(示例):

1
2
3
4
5
6
7
8
9
{
"iss": "[https://accounts.google.com](https://accounts.google.com)",
"sub": "1234567890",
"aud": "my-travel-app-id",
"exp": 1700000000,
"name": "John Doe",
"email": "john.doe@gmail.com",
"picture": "[https://example.com/photo.jpg](https://example.com/photo.jpg)"
}

4. 选择正确的身份验证

使用此矩阵来决定哪种方法适合您当前的项目架构。

身份验证类型 最适合 建筑
有状态(会话) 传统网络应用程序 服务器渲染(EJS、PHP、Django)。服务器在内存/数据库中跟踪用户。
无状态(JWT) 现代 API / SPA React、Vue、移动应用程序。服务器验证每个请求的令牌签名。
OAuth 2.0 / OIDC 社交登录 “使用 Google/GitHub 登录。”访问第 3 方数据。
API 密钥 系统集成 服务器到服务器或后台脚本。不涉及人工“登录”。

快速决策摘要

  1. 整体/简单网站? → 使用有状态会话
  2. 移动应用程序或 React 前端? → 使用 无状态 JWT