首页/文章/http-网络

OAuth 2.0是什么,以及如何正确接入

2026-10-15
5560 字约 19 分钟
...

一、先从一个需求理解 OAuth 2.0

假设你开发了一个相册整理工具,用户希望它读取某个云盘里的照片。

把云盘密码交给工具,会带来几个问题:工具拿到的权限太大,密码可能泄漏,用户也难以只撤销这个工具的访问。

OAuth 2.0 提供一套委托授权机制:用户在云盘自己的页面确认授权,工具取得一个有权限范围和有效期的访问令牌,再用它调用云盘 API。用户的云盘密码不交给工具。

可以把它理解为:给工具一张“限定用途的通行证”。但令牌泄漏同样可能导致数据被访问,所以授权之后仍需保护令牌。

OAuth 2.0 是协议框架,不是某个 SDK,也不是一段生成 Token 的代码。基础规范:RFC 6749

二、四个角色和常见参数

1. 四个角色

角色含义相册工具中的对应方
Resource Owner,资源所有者有权批准访问的一方用户
Client,客户端申请权限并调用 API 的应用相册工具;可包含自己的后端
Authorization Server,授权服务器处理授权并签发令牌云盘的授权服务
Resource Server,资源服务器提供受保护资源的 API云盘的照片 API

“客户端”是协议角色,并不特指浏览器。自己的后端也可以是 OAuth 客户端。角色定义

2. 参数速查

参数或凭证用途需要记住的边界
client_id标识注册的应用通常可以公开
client_secret某些客户端认证方式使用的密钥只能交给能保密的后端
redirect_uri授权结束后的回调地址使用预注册地址,不能随意跳转
scope申请的权限范围名称由平台定义,只申请必需权限
code一次性授权码短时有效,用于换令牌
access_token调用资源 API不能仅凭它推断用户登录身份
refresh_token向授权服务器换新访问令牌是否签发由平台决定
state关联发起和回调的授权事务随机、一次性,与浏览器会话绑定
code_verifier本次 PKCE 的随机秘密换令牌时提交,不放授权 URL
code_challengeverifier 经变换得到的挑战值授权请求中发送

其中 code 和 access_token 用途不同:前者用于兑换,后者用于调用 API。授权码流程、PKCE

三、OAuth、登录、JWT、Session 有什么关系

概念主要解决的问题
OAuth 2.0应用被允许访问哪些资源
OpenID Connect,OIDC在 OAuth 2.0 之上增加标准化的身份认证
JWT一种令牌表示格式
Session / Cookie网站如何维持自己的用户会话

OAuth 的核心是授权。“使用第三方账号登录”还需要可靠识别用户。

支持 OIDC 的平台,可以请求 openid scope,取得并验证 id_token。客户端应通过成熟库验证签名、可信签发者 iss、受众 aud、有效期;发送过 nonce 时还要验证它。使用 (iss, sub) 识别外部身份,不应仅凭昵称或邮箱自动合并账号。id_token 用于客户端确认身份,不应代替资源 API 的访问令牌。OIDC Core

对于不支持 OIDC 的平台,要遵循它明确规定的身份 API 和登录流程,不能把任意 access token 当成通用登录凭证。

JWT 也不等于 OAuth:访问令牌既可能是 JWT,也可能是不透明字符串。资源服务器按约定验证;客户端不应依赖解码 access token 来决定自己的登录状态。即便令牌是 JWT,Base64 解码也不等于验证成功。JWT 访问令牌规范

四、新项目应该选哪一种流程

场景适用方式注意点
有后端的网站授权码+PKCE,由后端处理令牌本文主线
纯浏览器 SPA授权码+PKCE是公共客户端,不能保管 client secret
手机 App、桌面应用授权码+PKCE使用系统浏览器,按平台规范接收回调
服务之间调用Client Credentials代表应用自身的权限,不代表某个用户登录
电视、部分 CLI 等输入受限设备Device Authorization Grant需要授权平台支持

公共客户端必须使用 PKCE;机密客户端也推荐使用。新接入使用 S256。不要把旧教程中的几种模式当作同等选项:安全最佳实践明确禁止密码授权模式,并建议避免隐式模式。RFC 9700

原生应用不应依靠内嵌一个固定 secret 来证明自身身份;应使用外部用户代理,通常就是系统浏览器。原生应用规范
设备授权流程允许用户在另一台设备的浏览器完成确认,设备端按规定轮询结果。设备授权规范

五、授权码+PKCE 的完整过程

下面采用后端保存令牌的 BFF 结构:浏览器只持有自己网站的会话 Cookie,后端作为 OAuth 客户端调用第三方 API。浏览器应用规范中的 BFF

sequenceDiagram
    actor U as 用户
    participant B as 浏览器
    participant C as 自己的后端
    participant A as 授权服务器
    participant R as 第三方资源API
    U->>B: 点击连接云盘
    B->>C: GET /oauth/start
    C->>C: 生成并保存 state、verifier
    C-->>B: 302 到授权地址,携带 challenge
    B->>A: 打开授权页面
    U->>A: 登录并同意权限
    A-->>B: 302 到回调地址,携带 code、state
    B->>C: GET /oauth/callback?code=...&state=...
    C->>C: 验证并消费授权事务
    C->>A: code+verifier,按注册方式认证客户端
    A-->>C: access_token,可选 refresh_token
    C->>C: 安全保存令牌,建立或更新本地会话
    C-->>B: 跳转到无敏感参数的结果页
    B->>C: 请求自己的业务接口
    C->>R: 携带 access_token 调用 API
    R-->>C: 返回资源
    C-->>B: 返回业务数据

浏览器转送的是授权码。此架构下,访问令牌通过后端与授权服务器之间的连接返回。

PKCE 防的是什么

如果授权码被截获,攻击者可能试图抢先拿它换令牌。PKCE 要求换码时额外提供本次事务的 code_verifier。

code_challenge = BASE64URL(SHA256(code_verifier))

授权时只发送 challenge,换码时才提交 verifier。授权服务器重新计算并比较,只有 code 通常不足以兑换。

verifier 使用密码学安全随机数,长度为 43~128 个允许字符;Base64URL 编码去掉 = 填充。PKCE 保护的是换码过程,不能阻止访问令牌被盗后使用,也不能替代 HTTPS。RFC 7636 §4

六、实际接入:按这六步做

第一步:到平台登记应用

确认并保存:

  • 应用类型、client_id、客户端认证方式。
  • 精确的回调地址,例如 https://app.example.com/oauth/callback。
  • 授权端点、令牌端点、资源 API、允许申请的 scope。
  • 是否支持 PKCE S256、OIDC、刷新及撤销令牌。

这些地址来自可信平台文档或经过校验的发现配置,不能从用户输入中任取一个 URL。示例中的 /authorize、/token 只是常见命名。安全最佳实践

第二步:生成本次事务的数据

下面这段可以用支持 base64url 编码的 Node.js 执行。它只生成 PKCE 和 state,不会登录任何真实平台。

import { randomBytes, createHash } from 'node:crypto';

const state = randomBytes(32).toString('base64url');
const verifier = randomBytes(32).toString('base64url');
const challenge = createHash('sha256')
  .update(verifier, 'ascii')
  .digest('base64url');

// 后端保存 state、verifier、过期时间、发起浏览器会话标识。
// 不要在生产日志中输出 verifier。

实现时可以用 Redis 或数据库保存约 5 分钟的事务记录;5 分钟是示例设计值,不是协议规定。记录需一次性消费,并支持同一浏览器并行发起的多个事务,避免后一次覆盖前一次。

state 必须与发起授权的浏览器会话绑定。只检查“这个随机值在 Redis 中存在”,仍可能接受从另一浏览器移来的回调。若使用 OIDC,还可生成独立 nonce,随事务保存,并在 ID Token 验证时检查。事务绑定要求

第三步:让浏览器前往授权页面

后端构造 URL,再返回 302。以下按多行展示,实际是一个经过 URL 编码的地址:

https://auth.example.com/authorize
  ?response_type=code
  &client_id=YOUR_CLIENT_ID
  &redirect_uri=https%3A%2F%2Fapp.example.com%2Foauth%2Fcallback
  &scope=photos.read
  &state=本次随机值
  &code_challenge=本次挑战值
  &code_challenge_method=S256

photos.read 是虚构的业务 scope,真实值以平台文档为准。OIDC 登录则按需请求 openid profile 等 scope,并加入本次 nonce。

前端按钮只需要导航到自己后端的 /oauth/start,再由后端跳转。不能期待 fetch() 跟随 302 后自动把整张网页切换成登录页。授权请求格式

第四步:处理回调并换令牌

成功回调的形式通常是:

https://app.example.com/oauth/callback?code=ONE_TIME_CODE&state=原来的随机值

后端处理顺序可以写成如下伪代码;其中会话、存储和 OAuth 库函数需要由项目实现:

GET /oauth/callback:
  读取 code、state、error,拒绝重复或格式异常的参数
  用 state 查找未过期事务,验证它属于当前浏览器会话
  原子地消费该事务,防止同一回调重复处理
  如有 error,显示授权失败或用户取消
  如缺少 code,结束处理
  按预先配置的授权平台,用 code 和 verifier 换令牌
  如果用于登录,验证身份,查找或创建对应的本地账号
  在服务端安全保存所需令牌,建立或更新本地会话
  跳转到固定的结果页,移除地址中的 code 和 state

服务端换码的请求体示例:

POST /token HTTP/1.1
Host: auth.example.com
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code&code=ONE_TIME_CODE&redirect_uri=https%3A%2F%2Fapp.example.com%2Foauth%2Fcallback&client_id=YOUR_CLIENT_ID&code_verifier=本次verifier

实际发送时使用表单编码工具生成正文,不要直接拼接字符串。

还要按登记方式认证客户端:机密客户端可能使用 client_secret_basic、private_key_jwt 等;公共客户端不携带 secret。不能照抄任一平台的认证方式到所有平台。交换时使用与授权请求相同的 redirect_uri。令牌请求

可能的响应:

{
  "access_token": "EXAMPLE_ACCESS_TOKEN",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "EXAMPLE_REFRESH_TOKEN",
  "scope": "photos.read"
}

3600 仅为示例;不能假定令牌总是一小时过期。refresh token 也不保证存在。应用应处理错误响应,并确认实际授予的权限满足功能要求。响应定义

第五步:用访问令牌调用 API

GET /v1/photos HTTP/1.1
Host: api.example.com
Authorization: Bearer EXAMPLE_ACCESS_TOKEN

Bearer 的含义是持有者即可使用。令牌应通过 HTTPS 放在请求头里,避免放到 URL、分析系统和日志中。Bearer Token 用法

自己的资源服务器也必须验证令牌:JWT 需验证可信签发者、签名、算法、有效期、受众等;不透明令牌可通过授权服务器提供的 introspection 确认状态和权限。之后仍要检查具体 scope、资源归属及业务权限。JWT 验证、Token Introspection

例如具备 photos.read,不代表可以读取任意用户的照片。OAuth 不能替代业务层的数据权限检查。

第六步:处理刷新、取消连接和退出

访问令牌过期且持有 refresh token 时,客户端向令牌端点提交:

Content-Type: application/x-www-form-urlencoded

grant_type=refresh_token&refresh_token=保存的刷新令牌

还需按平台要求认证客户端。若返回新的 refresh token,必须安全、原子地替换旧值。同一份凭证的并发刷新应协调,避免轮换时把正常请求误判为旧令牌重用。对于公共客户端,授权服务器必须通过刷新令牌轮换或发送方约束等方式检测重放。刷新安全要求

刷新失败且凭证已失效时,引导用户重新授权,避免无限重试。不要对所有 401 都盲目刷新;还可能是令牌受众不对、已被撤销等原因。

退出本网站、撤销第三方授权、退出第三方账号,是不同操作。清理自己的 Session 不会自动撤销第三方令牌;“取消连接”可按平台支持调用撤销端点,并删除本地凭证。已签发访问令牌的实际失效时间还取决于资源服务器验证及缓存策略。令牌撤销规范

七、前端与后端怎样分工

对有后端的 Web 项目,本文建议采用以下结构:

位置负责什么保存什么
浏览器发起导航、展示授权结果、请求自己的 API自己网站的会话 Cookie
自己的后端授权事务、换码、身份验证、刷新、调用第三方 APIverifier、客户端凭证、第三方令牌
授权平台用户认证、授权确认、签发令牌平台自己的授权数据

会话 Cookie 使用 HttpOnly、Secure,并按回调方式设置 SameSite。本文的跨站顶层 GET 回调通常可以使用 Lax;如果平台使用跨站 POST 回调,需要另行设计 Cookie 与关联机制。状态变更接口仍要防 CSRF,应用仍要防 XSS。BFF 减少浏览器直接暴露令牌的风险,但恶意脚本仍可能借用户会话发起请求。BFF 的安全边界

纯 SPA 则必须按公共客户端处理。不能把 secret 打进前端包;长期放在 localStorage 的令牌会暴露给同源脚本,不能把这种存储方式当成安全保证。浏览器客户端的威胁与架构

建议的本地数据关系如下,这是应用设计示例,并非 OAuth 强制表结构:

本地用户 users
  └─ 外部身份 external_identities:user_id、issuer、subject
  └─ 第三方授权 connections:user_id、provider、scope、令牌、过期时间
  └─ 本地会话 sessions:session_id、user_id、过期时间

令牌按凭证处理,限制读取权限并保护存储。若后端还需要拿原始令牌调用第三方接口,就不能只保存不可逆哈希。连接第三方账号时,绑定到当前已认证用户;合并账号另设明确的验证流程。

八、如果要自己提供 OAuth 授权服务

“接入第三方 OAuth”与“建设授权服务器”是两个工作量不同的任务。后者至少包括:

  • 客户端登记、回调白名单和客户端认证。
  • 用户认证、授权同意、scope 管理。
  • 授权码的短时有效、一次性消费及 PKCE 校验。
  • 令牌签发、受众限制、刷新、撤销和密钥轮换。
  • 资源服务器验证、审计,以及对应的攻击防护。

建议选择维护中的授权服务器或成熟协议库,按规范配置,再实现自己的业务权限。实现一个 /login 返回 JWT 的接口,并不意味着已经实现 OAuth 2.0。OAuth 基础框架、安全最佳实践

九、常见问题排查

现象优先检查
redirect_uri_mismatch 或类似错误协议、域名、端口、路径、末尾斜杠与登记地址是否一致
invalid_grantcode 是否过期或重复使用、verifier 是否对应、回调地址是否一致;刷新时检查刷新凭证
invalid_client应用凭证、环境、客户端认证方法是否正确
invalid_scopescope 是否存在、应用是否获准申请
state 校验失败Cookie 是否带回、事务是否过期、浏览器会话是否对应、多标签是否互相覆盖
API 返回 401令牌有效性、issuer、audience、撤销状态、请求头
API 返回 403scope、资源归属与业务权限;以平台错误说明为准
浏览器报 CORS当前架构是否要求浏览器跨域调用;BFF 通常由后端调用第三方 API

错误名称和 HTTP 状态的细节可能因平台而异,先读平台返回的错误类型,不要把所有错误都归因于“Token 过期”。分享日志时遮盖 code、secret 和各类令牌。协议错误、资源访问错误

十、接完以后,怎么确认真的做对了

至少走一遍这些情形:

  • 正常授权:能拿到预期范围的资源;如果用于登录,本地账号映射正确。
  • 用户拒绝:能回到可理解的失败页。
  • 篡改或遗漏 state、换一个浏览器回调:请求被拒绝。
  • 使用错误 verifier:换码失败。
  • 重放同一个回调或 code:不能再次成功。
  • 令牌过期:能按策略刷新;刷新失效时能重新授权。
  • 多标签授权、并发 API 请求:事务和刷新状态不会互相覆盖。
  • 取消连接:本地凭证删除,平台撤销按其能力执行。
  • 检查地址栏、前端包、日志:没有泄漏 secret 或令牌。

本文提供的是协议说明、请求示例和接入设计;未使用真实平台账号完成端到端授权。实际接入必须用该平台的测试应用验证以上流程。

十一、复习时回答这五个问题

  1. OAuth 2.0 解决什么? 应用如何在限定权限下访问资源。
  2. 为什么先拿 code? 把授权跳转与令牌兑换分开,便于约束和验证兑换者。
  3. 为什么加 PKCE? 把本次授权码兑换绑定到持有 verifier 的事务。
  4. 为什么拿到 Token 不等于登录完成? 还需要验证身份来源并建立自己的账号和会话。
  5. 什么情况下用 Client Credentials? 应用以自身权限调用服务,不是替某个用户登录。

延伸阅读

已有笔记

官方规范

如果您觉得这篇文章有帮助,请点个赞吧~

分享文章

相关文章

更多文章 →
http-网络2025-10-27
frp-内网穿透详解
1\. 简介与工作原理 frp(Fast Reverse Proxy)是一个高性能的反向代理 / 内网穿透工具。典型部署模式: 在 公网 VPS 上运行 (服务端),监听来自 的 出站持久连接 (控制端口,通常 7000)。 在 内网机器 上运行 (客户端),与 frps 建立长连接并注册若干映射(如 tcp/http)。 当外部请求到达 frps(公网),frps 根据路由规则把流量转发回对应的 frpc 连接,再由 frpc 转发到...
学习
http-网络2025-10-13
内网穿透介绍
  1\. 简要概念与为什么需要内网穿透 大多数家庭/公司网络在私有网(RFC1918)后面,通过 NAT 共享一个公网 IP。NAT 允许内网对外发起连接,但阻止公网主动连入。 内网穿透 就是建立从公网到内网的可达通道,常用于:Webhook 回调、远程演示、远程管理、IoT 设备接入、远程调试等。 2\. 核心原理(三行总结) 反向连接 :内网主动连到公网穿透服务器,建立一条"回路"。 中继/代理 :穿透服务器把外部流量中...
学习
http-网络2025-08-06
按下回车后网页是怎么出来的
当你在浏览器中输入一个网址,比如 ,然后按下回车键,几秒钟后页面就完整地呈现在你眼前。这个看似简单的操作,背后其实是一整套精密协作的网络机制在起作用。本文将带你从基础讲起,理解 Web 开发中最重要的网络知识,帮助你搞清楚: 数据是如何从服务器传到你的电脑,并最终变成网页的? 一、互联网通信的分层模型:OSI 七层模型 为了管理复杂的网络通信,工程师们提出了一个通用的参考模型—— OSI 七层模型 。它把整个通信过程划分为七个层次,每一...
学习面试
http-网络2025-06-20
MQTT与HTTP在物联网中的比较:为什么MQTT是更好的选择
简介: 通过上述分析,可以看出MQTT在物联网应用中的确是更好的选择。其高效的通信模型、低带宽消耗、稳定的连接保持机制以及可靠的消息质量保证,使其在各种物联网场景中都能表现出色。开发者在设计和实现物联网系统时,应优先考虑采用MQTT协议,以充分发挥其在资源受限环境下的优势,提升系统的整体性能和可靠性。 【杂谈】 MQTT与HTTP在物联网中的比较:为什么MQTT是更好的选择 在物联网(IoT)应用中,选择合适的通信协议是实现高效、可靠数...
学习
http-网络2025-03-04
网络代理详解
一、 的基础概念 1\. 什么是代理? 代理是一种通过中间服务器来帮助 访问目标服务器的技术。代理服务器位于客户端与服务器之间,充当两者的中介,从而实现客户端和服务器之间的隔离。用户的所有请求经过代理后,再发往目标服务器,而目标服务器的响应则返回代理,再由代理传递给用户。 2\. 网络代理的工作流程 在基本的网络代理工作流程中, 扮演了“请求转发者”和“响应返回者”的角色: 1. 客户端请求 :客户端将请求发送到代理服务器,而不是直接发...
学习面试
http-网络2024-11-29
没有koa的cors你怎么处理跨域?
关联问题: JSONP有何局限性 CORS如何处理复杂请求 代理在生产环境效果如何 前言 “金九银十”的秋招,就像是落进池塘的羽毛,连点水花都没砸起来。已读不回是常态,能给面试已是情分,能拿offer的更是少之又少。可怜我在寒潮来临的深秋之际,也只得哀叹一句“越来越冷了啊...” 不过人总是要吃饭的,所以再怎么冷,卖炭翁也得卖炭,牛马攻城狮也得“攻城”,既是为了填饱肚子,也是为了“春天”做准备。所以今天为各位前端工程狮细聊一下前端面试中...
学习面试

评论

请登录后发表评论

去登录
加载评论中...

目录