小白也能看懂的登录、认证、授权、JWT、OAuth2 和 SSO

Published

Tags: Reading notes

先从一个生活场景开始 #

想象你去一栋办公楼办事。

你走到前台,前台问:“你是谁?”你拿出身份证或员工卡。前台核对以后确认:“你确实是张三。”这一步叫 认证,英文是 Authentication

接着你要去 18 楼财务室。保安查了一下系统,说:“你是张三没错,但你没有权限进财务室,只能去 6 楼会议室。”这一步叫 授权,英文是 Authorization

这两个词最容易混:

登录系统时输入用户名和密码,主要是在做认证。认证成功以后,系统再判断你是不是管理员、能不能看订单、能不能删用户,这才是授权。

一次请求进系统时发生了什么 #

假设你打开一个网站,点击“我的订单”。浏览器会向后端发请求:

GET /api/orders

后端不能直接把订单返回给你。它至少要问三个问题:

  1. 你有没有带凭证?
  2. 这个凭证能不能证明你是谁?
  3. 你有没有权限查看这些订单?

如果你没登录,系统通常返回:

401 Unauthorized

这个名字有点误导。401 Unauthorized 在 HTTP 语境里通常表示“你还没有通过认证”,也就是系统还不知道你是谁。

如果系统知道你是谁,但你没有权限,常见返回是:

403 Forbidden

也就是“我知道你是谁,但这件事你不能做”。

凭证是什么 #

凭证就是用来证明身份或访问资格的东西。

常见凭证包括:

不同系统用不同凭证,但目标类似:让服务器能判断请求者是否可信。

Basic Auth:最朴素的用户名密码认证 #

Basic Auth 是最简单的 HTTP 认证方式。

客户端把用户名和密码拼在一起:

alice:my-password

然后做 Base64 编码,放进请求头:

Authorization: Basic YWxpY2U6bXktcGFzc3dvcmQ=

服务器收到后解码,再检查用户名和密码。

它的问题是:Base64 不是加密,只是编码。任何人拿到这段内容都能还原出用户名和密码。所以 Basic Auth 必须使用 HTTPS,否则很危险。

Basic Auth 现在仍然会出现在:

但它不适合作为现代大型应用的主要登录方案。

Digest Auth:试图改进 Basic Auth 的老方案 #

Digest Auth 的思路是:不要直接传密码,而是传一个摘要值。

大致流程是:

  1. 客户端请求资源。
  2. 服务端返回 401,并给客户端一个 challenge。
  3. 客户端用用户名、密码、challenge 等信息计算摘要。
  4. 服务端验证摘要是否正确。

它比 Basic Auth 好一点,因为不直接发送密码。但 Digest Auth 依然比较老,早期版本依赖 MD5,而 MD5 已经不适合作为现代安全基础。

现在新系统通常不会优先选 Digest Auth。

API Key:给程序用的钥匙 #

API Key 可以理解成“给程序用的一把钥匙”。

比如你注册一个地图服务,平台给你一个 key:

map_live_abc123

你调用地图 API 时带上它:

X-API-Key: map_live_abc123

或者:

Authorization: Bearer map_live_abc123

服务器收到 key 后,会去数据库或缓存里查:

API Key 的优点是简单,特别适合机器访问 API。

它的缺点也明显:

API Key 适合服务到服务、开发者平台、自动化脚本,但不适合作为普通用户网页登录的完整方案。

HTTP 本身是无状态的。

也就是说,你第一次请求网站和第二次请求网站,服务器天然并不知道这是同一个人。为了“记住你”,传统 Web 应用通常使用 session 和 cookie。

流程如下:

  1. 你输入用户名和密码登录。
  2. 服务器验证成功。
  3. 服务器创建一条 session 记录,例如 session_id = abc123
  4. 服务器把 session_id 放进 cookie 发给浏览器。
  5. 浏览器之后每次访问同一个网站,都会自动带上这个 cookie。
  6. 服务器用 session_id 去查 session store。
  7. 查到了,就知道你是谁。

响应里可能有这样的头:

Set-Cookie: session_id=abc123; HttpOnly; Secure; SameSite=Lax

后续请求里浏览器会自动带上:

Cookie: session_id=abc123

session 可以放在:

生产环境里常见的是 Redis,因为它快,也支持自动过期。

Session 的关键特点是:服务器要保存状态。

这很适合传统网站。但在微服务、分布式系统、多区域部署里,所有服务都要共享或识别同一套 session,复杂度会变高。

如果 cookie 里放的是 session id 或 refresh token,就要认真设置安全属性。

常见设置:

Set-Cookie: session_id=abc123; HttpOnly; Secure; SameSite=Lax; Path=/

几个属性的意思:

不要把 session id 当成普通字符串随便塞到页面里。session id 一旦泄露,攻击者可能直接冒充用户。

Token:把“通行证”交给客户端 #

Token-based authentication 是现代 API 常见模式。

流程是:

  1. 用户登录。
  2. 服务器验证用户名和密码。
  3. 服务器签发一个 token。
  4. 客户端之后请求 API 时带上 token。
  5. 服务器验证 token。

请求头通常长这样:

Authorization: Bearer <token>

这里的 Bearer 可以理解成“持有人”。Bearer token 的意思是:谁拿着这个 token,谁就能访问。

所以 Bearer token 一旦泄露,就很危险。

JWT:一种常见的 token 格式 #

JWT 是 JSON Web Token 的缩写。

它通常长这样:

xxxxx.yyyyy.zzzzz

三个部分分别是:

Payload 可能长这样:

{
"sub": "user_123",
"email": "alice@example.com",
"role": "admin",
"exp": 1777000000
}

这里:

JWT 的重要特点是:它可以自包含。

服务器收到 JWT 后,可以验证签名。如果签名有效,说明 token 没被改过。如果还没过期,服务器就可以从 payload 里读出用户身份。

这带来一个好处:服务器不一定每次都查数据库。

但也带来问题:

所以 JWT 适合短期 access token,不适合无限期使用。

Access Token 和 Refresh Token #

现代登录系统通常会把 token 分成两类:

为什么要两个?

因为 access token 每次都要拿出去用,暴露面更大,所以应该短命。refresh token 更敏感,使用频率更低,可以用更严格的方式保存。

典型流程:

  1. 用户登录。
  2. 服务器返回 access token 和 refresh token。
  3. 客户端用 access token 调用 API。
  4. access token 过期后,API 返回 401
  5. 客户端用 refresh token 请求新的 access token。
  6. 用户不用重新输入密码。

常见有效期:

浏览器应用里,refresh token 通常建议放在 HttpOnly + Secure + SameSite cookie 里。不要随手放进 localStorage,因为一旦页面出现 XSS,JavaScript 可以读取 localStorage。

更成熟的系统还会做 refresh token rotation:每次刷新时发一个新的 refresh token,并让旧的失效。这样可以降低长期 token 被偷后的风险。

OAuth2:不是登录,而是授权 #

OAuth2 是最容易被误解的概念之一。

很多人说“用 OAuth2 登录”,这在日常表达里可以理解,但严格说 OAuth2 本身主要解决的是授权,不是认证。

OAuth2 回答的问题是:

某个应用能不能代表用户访问某些资源?

例子:你使用一个在线文档工具,它想读取你的 Google Drive 文件。

流程大概是:

  1. 文档工具把你重定向到 Google。
  2. Google 问你:“是否允许这个应用读取你的 Drive 文件?”
  3. 你点击允许。
  4. Google 给文档工具一个 authorization code。
  5. 文档工具拿 code 去换 access token。
  6. 文档工具用 access token 调 Google Drive API。

这个 access token 证明的是:

这个应用被允许访问某些资源。

它不一定证明:

当前用户是谁。

这就是 OAuth2 和登录认证的区别。

OAuth2 里常见角色:

OpenID Connect:在 OAuth2 上补上“你是谁” #

OpenID Connect,简称 OIDC,是建立在 OAuth2 之上的身份层。

OAuth2 给 access token,让应用访问资源。OIDC 额外引入 ID token,让应用知道用户是谁。

当你点击“Sign in with Google”时,背后常见的是 OIDC。

流程大致是:

  1. 你的应用把用户跳转到 Google。
  2. 用户在 Google 登录。
  3. Google 返回 authorization code。
  4. 应用用 code 换 token。
  5. Google 返回 access token 和 ID token。
  6. 应用验证 ID token。
  7. 应用从 ID token 里读出用户身份。

ID token 通常也是 JWT,里面可能有:

{
"sub": "google-user-id",
"email": "alice@gmail.com",
"name": "Alice"
}

所以:

SSO:登录一次,到处可用 #

SSO 是 Single Sign-On,单点登录。

它不是一种具体协议,而是一种体验:

用户登录一次,就能访问多个系统。

比如你登录公司账号以后,可以访问:

这些系统不一定自己保存你的密码,而是信任同一个身份提供方,比如 Okta、Microsoft Entra ID、Google Workspace。

SSO 背后常用协议包括:

SAML:企业系统里常见的老牌 SSO 协议 #

SAML 是 Security Assertion Markup Language。

它是基于 XML 的身份协议,在企业系统里非常常见。

典型流程:

  1. 你访问 Salesforce。
  2. Salesforce 发现你没登录,把你重定向到公司身份系统。
  3. 你在公司身份系统登录。
  4. 身份系统返回一个 SAML assertion。
  5. Salesforce 验证 assertion。
  6. 验证通过后,你进入 Salesforce。

SAML 比 OIDC 更老,也更重,但仍然广泛用于企业、政府、教育和遗留系统。

OIDC 更现代,更适合 Web、移动端和 API 生态。

常见误区 #

误区一:JWT 是一种认证方式。

更准确地说,JWT 是一种 token 格式。它可以用于认证系统,也可以用于信息交换,但它本身不是完整登录方案。

误区二:Bearer token 就是 JWT。

Bearer 是 token 的使用方式。JWT 是 token 的一种格式。Bearer token 可以是 JWT,也可以只是随机字符串。

误区三:OAuth2 是登录协议。

OAuth2 主要是授权框架。真正补上用户身份认证的是 OpenID Connect。

误区四:有了 JWT 就完全不需要数据库。

不一定。如果你要支持主动登出、封号、权限实时变化、token 撤销、设备管理,仍然可能需要数据库、缓存、黑名单或 token version。

误区五:把 token 放 localStorage 很方便,所以没问题。

方便不等于安全。localStorage 可以被同源 JavaScript 读取,一旦出现 XSS,token 容易被偷。浏览器场景下,敏感长期凭证更常见的做法是使用安全 cookie。

新手怎么选方案 #

如果你做的是普通服务端渲染网站:

如果你做的是前后端分离 Web 应用:

如果你做的是移动 App:

如果你做的是开放平台 API:

如果你要做“使用 Google 登录”:

如果你接企业客户:

一张总表 #

名词它是什么主要解决什么
Authentication认证确认你是谁
Authorization授权决定你能做什么
Basic AuthHTTP 认证方式用用户名密码证明身份
Digest AuthHTTP 认证方式用摘要减少明文密码传输
API Key凭证让程序调用 API
Cookie浏览器机制自动保存和发送小段数据
Session服务端状态记住登录用户
Bearer Tokentoken 使用方式持有者即可访问
JWTtoken 格式携带 claims 并支持签名
Access Token短期访问凭证调用 API
Refresh Token长期续期凭证换新的 access token
OAuth2授权框架允许应用代表用户访问资源
OpenID Connect身份协议在 OAuth2 上确认用户身份
SSO登录体验登录一次访问多个系统
SAML身份协议企业 SSO 身份断言

最后用一句话记住 #

认证是“你是谁”,授权是“你能做什么”;session 是服务器记住你,token 是你带着通行证来;JWT 是一种通行证格式,OAuth2 是授权别人办事,OIDC 是用 OAuth2 做登录身份,SSO 是登录一次到处可用。

延伸资料 #