在现代身份认证与授权体系中,OAuth 2.0 和 OpenID Connect(OIDC) 经常被一起提及,但它们解决的是不同层面的问题。理解两者的区别和协作关系,是构建安全、标准的身份系统的关键。
是什么?
OAuth 2.0 不是身份验证协议,而是一个授权框架,允许第三方应用在用户授权下访问受保护资源,而无需获取用户密码。
OpenID Connect(OIDC)是一种基于OAuth 2.0协议构建的身份认证标准,旨在为现代应用程序提供简单、安全且可互操作的用户身份验证方式。它通过在OAuth 2.0的授权框架之上添加一个身份层,解决了OAuth 2.0本身不完善的身份认证问题。
核心角色:
- Resource Owner(资源所有者):通常是用户自己
- Client(客户端):请求访问用户资源的应用(Web、移动、单页应用SPA 等)
- Authorization Server(授权服务器):颁发令牌(如 Casdoor、Keycloak、Google OAuth)
- Resource Server(资源服务器):托管用户数据(如 API 服务),接受并验证访问令牌,以确定请求是否合法
重要区分:
- OAuth 2.0 = 授权
- OpenID Connect = 身份认证+授权
OAuth2.0授权流程
OAuth2 定义了好几种授权模式,有Authorization Code、Client Credentials、Device Code、Implicit Flow和Password Credentials,隐式流和密码凭据模式被认为是不安全的,不推荐使用。
授权码模式(Authorization Code Flow)
适用于:服务器端 Web 应用、移动 App、单页应用SPA(配合 PKCE 使用)
sequenceDiagram
participant User as 用户
participant Client as 客户端
participant AS as 授权服务器
participant RS as 资源服务器
User->>Client: 访问应用
Client-->>User: 重定向至授权服务器<br>(client_id, redirect_uri, scope, state, code_challenge)
User->>AS: 登录并同意授权
AS-->>User: 重定向回客户端<br>(?code=xxx&state=yyy)
User->>Client: 浏览器携带授权码
Client->>AS: POST /token<br>(code, client_id, client_secret*, code_verifier)
AS-->>Client: 返回 access_token (+ refresh_token)
Client->>RS: GET /api/data<br>Authorization: Bearer <access_token>
RS-->>Client: 返回受保护资源
流程
-
用户访问客户端程序,如单页应用
-
将用户请求重定向至包含一些参数的授权URL
-
response_type 授权模式
-
client_id 授权服务器上注册的客户端ID
-
redirect_uri 重定向地址,返回授权码给客户端
-
scope 请求的权限
-
state 随机字符串,防止CSRF攻击,存储在客户端会话中,并在收到回调时进行验证
-
-
授权链接就是授权服务器的地址,没登陆时就是登陆页面,然后授权页面。一般显示哪个应用正在请求哪些权限,以及被授予哪个账户。用户批准后授权服务器生成一个授权码
-
授权服务器重定向到应用的redirect_uri,并且携带授权码
-
应用携带授权码、客户端ID、客户端凭证向授权服务器令牌端点发起POST请求,以换取AccessToken(这次请求是服务器之间进行,不会暴露给浏览器)
-
授权码有效未过期的话,授权服务器响应AccessToken
一般会有如下字段:
1 2 3 4 5 6 7 8{ "access_token": "ACCESS_TOKEN", "token_type": "bearer", "expires_in": 123456789, "refresh_token": "REFRESH_TOKEN", "scope": "read", "uid": 111111 }
🔐 安全机制:
- 授权码和访问令牌分离传输,访问令牌不会通过浏览器传输
state参数防 CSRF- 授权码一次性 + 短有效期(≤10分钟)
📌 适用场景:
- 所有新项目(无论是否能存储
client_secret)- 移动 App(使用系统浏览器 + PKCE)
- SPA(必须用 PKCE,避免隐式模式)
PKCE
PKCE 是授权码流程的安全扩展,防止授权码拦截攻击。对于无法安全存储客户端秘密的公共客户端(移动应用、单页应用)来说,这非常重要
步骤:
-
客户端生成
-
code verifier:随机字符串
-
code challenge:对上面的verifier哈希
-
-
授权请求,带上code challenge和其hash方法
-
拿到授权码后,服务器请求token时,需要带上原始的verifier
-
服务器验证
-
使用指定的方法对verifier进行hash
-
与存储的challenge比较
-
客户端凭证模式(Client Credentials Grant)
适用于:服务到服务通信(无用户参与)。
sequenceDiagram
participant Client as 客户端(服务A)
participant AS as 授权服务器
participant RS as 资源服务器(服务B)
Client->>AS: POST /token<br>grant_type=client_credentials<br>client_id + client_secret
AS-->>Client: 返回 access_token
Client->>RS: 请求资源<br>Authorization: Bearer <access_token>
RS-->>Client: 返回资源(属于客户端自身)
🔐 安全机制:
- 仅验证客户端身份,不涉及用户
- 必须通过 HTTPS 传输
client_secret- 作用域(scope)严格限制
📌 适用场景:
- 微服务间
- 后台任务
- 无用户参与的自动化流程
设备码模式(Device Code Grant)
适用于:无完整输入/显示能力的设备(如智能电视、IoT 设备)。
sequenceDiagram
participant Device as 设备(如电视)
participant AS as 授权服务器
participant User as 用户(手机/电脑)
participant RS as 资源服务器
Device->>AS: POST /device/code<br>client_id
AS-->>Device: 返回 device_code, user_code, verification_uri
Device->>User: 显示“请访问 verification_uri 并输入 user_code”
User->>AS: 在另一设备登录并输入 user_code
AS-->>User: 用户确认授权
Device->>AS: 轮询 /token?grant_type=device_code&device_code=...
AS-->>Device: 返回 access_token
Device->>RS: 使用令牌访问资源
RS-->>Device: 返回数据
流程:
-
设备向授权服务器请求设备码和用户码
-
设备显示用户码和验证 URL
-
用户在另一设备上访问验证 URL 并输入用户码
-
用户登录并授权
-
设备定期轮询授权服务器获取访问令牌
🔐 安全机制:
user_code为人类可读短码(如ABCD-EFGH)device_code长期随机字符串- 短有效期(通常 10-30 分钟)
- 轮询间隔由服务器控制(防暴力破解)
📌 适用场景:
- 智能电视、游戏主机
- 命令行工具(如
gh auth login)- 无浏览器的嵌入式设备
❌ 已弃用的授权模式
⚠️ 行业共识:IETF OAuth 2.0 Security Best Current Practice (BCP) 已明确弃用上述两种模式。
| 模式 | 问题 | 替代方案 |
|---|---|---|
| 隐式模式(Implicit) | 令牌直接暴露在 URL 中,易被 XSS、日志、referrer 泄露 | 授权码 + PKCE |
| 密码模式(Password Credentials) | 应用需收集用户密码,违反 OAuth 核心原则 | 授权码 + PKCE |
常见安全威胁与防护
1. 授权流程类威胁
| 威胁 | 防护措施 |
|---|---|
| 授权码截获 | 强制 PKCE;授权码一次性 + 短有效期 |
| 开放重定向 | redirect_uri 精确白名单匹配(禁用通配符) |
| CSRF 攻击 | 必须使用随机 state 参数并严格校验 |
2. 令牌管理类威胁
| 威胁 | 防护措施 |
|---|---|
| XSS 窃取令牌 | Web 应用使用 HttpOnly + Secure + SameSite=Strict Cookie(BFF 架构) |
| Refresh Token 滥用 | 实施轮转机制(每次使用后旧 token 失效);设置 ≤30 天有效期 |
| 令牌长期有效 | Access Token 短期有效(30-60 分钟);支持吊销 |
3. 客户端实现类威胁
| 威胁 | 防护措施 |
|---|---|
| PKCE 缺失 | 所有公共客户端(SPA/移动)必须启用 PKCE |
| Client Secret 泄露 | 公共客户端绝不使用 client_secret;机密客户端存于密钥管理服务 |
| Clickjacking | 授权页面设置 X-Frame-Options: DENY |
OIDC
在OAuth2.0的基础上,标准化身份信息格式,必须有用户参与登陆。
OIDC复用OAuth2.0的授权码流程,仅增加了两个参数:
-
请求:scope=openid profile email,必须包含openid(这是OIDC的标志),其他是可选获取更多用户信息
-
响应:额外返回id_token
id_token是JWT格式的,包含标准声明(claims),由授权服务器签名(通常RS256),客户端可以本地验证,无需调用服务器。
ID Token结构
|
|
OIDC的几个概念说明
| 概念 | 说明 |
|---|---|
| OpenID Provider (OP) | 实现 OIDC 的授权服务器(如 Casdoor、Keycloak、Google) |
| Relying Party (RP) | 使用 OIDC 进行登录的客户端(即你的应用) |
| ID Token | JWT 格式的身份令牌,证明用户已认证 |
| UserInfo Endpoint | 可选:通过 access_token 获取更详细的用户信息(GET /userinfo) |
| Discovery Document | 标准化元数据端点(.well-known/openid-configuration),自动发现 OP 配置 |
授权流程
大致和OAuth2.0授权码模式一样
-
发起授权请求
-
处理回调,交换令牌
-
验证ID Token(关键步骤)
-
拿access_token获取用户详细信息(可选)
sequenceDiagram
participant User as 用户
participant Client as 客户端
participant AS as 授权服务器(OIDC Provider)
participant RS as 资源服务器
User->>Client: 访问应用
Client-->>User: 重定向至 AS<br>scope=openid+profile+email<br>response_type=code<br>client_id=xxx
User->>AS: 登录并同意
AS-->>User: 重定向回客户端<br>?code=AUTH_CODE
User->>Client: 浏览器携带 code
Client->>AS: POST /token<br>grant_type=authorization_code<br>code=AUTH_CODE<br>client_id/secret
AS-->>Client: 返回<br>access_token + id_token(JWT)
Client->>Client: 验证 id_token 签名 & claims
Client->>RS: 用 access_token 请求用户数据
RS-->>Client: 返回资源
最佳实践
- 统一使用授权码 + PKCE:无论客户端类型,这是当前最安全的通用方案。
- 一般不手写 OAuth 流程:使用成熟库。
- 前端 0 令牌原则:通过 BFF(Backend for Frontend)架构,让浏览器只持 Cookie,令牌由后端管理。
- 最小权限原则:仅请求必要 scope,定期审查权限。
- 全链路 HTTPS:从授权到资源访问,全程 TLS 加密。
- 验证重定向URI:精确匹配注册的URI,不使用通配符。
- 监控与吊销:记录授权行为,支持用户一键撤销所有会话。