---
title: OAuth2 与 OpenID Connect
url: https://doc.liz6.com/networking/12-security-protocols/02-oauth2-and-openid-connect
locale: zh
area: networking
tags:
- networking
- 安全协议
date: 2026-06-30
modified: 2026-06-30
description: OAuth2 是授权框架(让应用以你的名义访问你的资源),OIDC 在它之上加了一层认证(让应用确认你是谁)。authorization code + PKCE 是当前最佳实践,implicit flow 已死。
---

# OAuth2 与 OpenID Connect

> OAuth2 是授权框架(让应用以你的名义访问你的资源),OIDC 在它之上加了一层认证(让应用确认你是谁)。authorization code + PKCE 是当前最佳实践,implicit flow 已死。

## 概述

OAuth2（RFC 6749, 2012）是互联网授权的标准框架——"让第三方应用访问我的资源，但不需要我的密码"。核心是 Authorization Code + PKCE 流程：浏览器重定向到授权服务器 → 用户登录 → 浏览器携带 code 返回 → client 用 code 换 access token。OIDC（OpenID Connect, 2014）在 OAuth2 上加入身份认证（id_token），成为"Login with Google/GitHub"的基础。JWT 是 OAuth2 最常用的 access token 格式——自包含、可验证、无状态。

## OAuth2: 授权 (不是认证)

```
四个角色:
  Resource Owner: 用户
  Client: 第三方应用
  Authorization Server: 授权服务器
  Resource Server: API 服务器

Authorization Code + PKCE (唯一推荐的 web flow):
  1. Client: 生成 code_verifier (随机), code_challenge = SHA256(verifier)
  2. Browser redirect:
     GET /authorize?response_type=code
       &client_id=grafana
       &redirect_uri=https://grafana.example.com/login/callback
       &scope=openid profile email
       &state=random123
       &code_challenge=<base64url(challenge)>
       &code_challenge_method=S256
  3. User 登录 + consent
  4. Browser redirect back: https://grafana.example.com/login/callback?code=AUTH_CODE&state=random123
  5. Client → Authorization Server (backchannel):
     POST /token
     grant_type=authorization_code
     &code=AUTH_CODE
     &redirect_uri=...
     &code_verifier=<verifier>  ← server 验证 SHA256(verifier)==challenge
  6. Response: { "access_token", "refresh_token", "id_token", "token_type":"Bearer", "expires_in":3600 }
  7. Client: 用 access_token 请求 API (Authorization: Bearer <token>)
```

## PKCE 为什么需要

传统 flow 中 `code` 可以被拦截 (mobile app 的 redirect URI 可能被其他 app 注册)。PKCE 保证即使 code 泄露，没有 `code_verifier` 也换不来 token。

## JWT

```
格式: base64(header).base64(payload).signature

Header: {"alg":"RS256","typ":"JWT","kid":"key-2025"}
Payload (claims):
  iss: 签发者
  sub: 用户 ID (subject)
  aud: 接收方 (audience, 此 token 给谁用)
  exp: 过期时间 (unix)
  iat: 签发时间
  email, name, groups: 自定义 claims

Signature: RSA-SHA256(base64(header)+"."+base64(payload))

验证:
  1. 获取 JWKS (https://auth.example.com/.well-known/jwks.json) → 取 kid 对应公钥
  2. 验证签名
  3. 检查 exp (未过期), aud (此 token 是给我的), iss (来自信任的 issuer)
```

## OpenID Connect (OIDC)

OIDC = OAuth2 + 用户身份 (`id_token`):

```
id_token (JWT): 给 client 证明"用户是谁" (不是用来调 API 的)
access_token:   给 API 证明"client 有权访问" (不包含用户信息)
```

## Client Credentials (机器间)

```
POST /token
  grant_type=client_credentials
  &client_id=my-app
  &client_secret=secret

→ { "access_token":"...", "expires_in":3600 }
→ 没有用户参与
```

## 参考

- **jwt.io**: debug JWT
- **Keycloak**: open source identity provider
- **OIDC playground**: openidconnect.net

*Keywords: OAuth2, PKCE, JWT, OIDC, id_token, client credentials, JWKS, Keycloak*
