OpenID Connect的认证与授权详解

OAuth 2.0是一种授权机制,用户通过授权服务器给第三方应用授权,以访问自己的资源(位于资源服务器)。相对于OAuth 1.0,OAuth 2.0简化了对客户端的验证,只保留了获取Authorization Code和Access Token的请求过程。

OpenID Connect是OAuth 2.0的一个扩展,结合OAuth 2.0的授权提供了用户的认证。

常见的认证方式

基于OAuth 2.0的认证/授权协议

复制代码
| 协议                                    | 说明                                        |
|-----------------------------------------|--------------------------------------------|
| OpenID Connect                          | OAuth 2.0 + 身份认证(ID Token)              |
| UMA (User-Managed Access)               | OAuth 2.0 + 授权管理(资源所有者控制访问策略)  |
| OAuth 2.0 Token Introspection           | OAuth 2.0 + 令牌验证标准(RFC 7662)           |
| OAuth 2.0 Dynamic Client Registration   | OAuth 2.0 + 动态客户端注册(RFC 7591)        |
| OAuth 2.0 Authorization Server Metadata | OAuth 2.0 + 服务发现(RFC 8414)              |

非OAuth 2.0的认证协议

javascript 复制代码
| 协议                  | 说明                  |
|-----------------------|----------------------|
| SAML 2.0              | XML格式,企业级 SSO |
| LDAP/Active Directory | 目录服务认证          |
| Kerberos              | 网络认证协议          |
| OpenID 1.0/2.0        | OAuth 2.0前身(已废弃) |

常见的Token格式

  • JWT(JSON Web Token)
  • Reference Token (随机字符串)
  • SAML Token(XML 格式)
  • MAC Token(OAuth 1.0专用,已废弃)

结合Authorization Code + PKCE的OpenID Connect的认证与授权

PKCE (Proof Key for Code Exchange),通过 code_verifier 和 code_challenge 确保只有原始客户端能获得Access Token。

客户端准备
javascript 复制代码
// 客户端生成 code_verifier 和 code_challenge
const code_verifier = generateRandomString(43);  // 43-128 字符
const code_challenge = base64UrlEncode(sha256(code_verifier));

// 保存 code_verifier(sessionStorage 或内存)
sessionStorage.setItem("code_verifier", code_verifier);
用户点击登录

客户端向认证/授权服务器发出HTTP请求如下:

javascript 复制代码
GET /authorize HTTP/1.1
Host: auth.server.com

response_type=code&
client_id=s6BhdRkqt3&
redirect_uri=https://client.example.com/callback&
scope=openid profile email&
state=af0ifjsldkj&
code_challenge=dGhpcyBpcyBhIGNoYWxsZW5nZQ&
code_challenge_method=S256
认证/授权服务器对用户进行认证/授权
  1. 认证/授权服务器显示登录页面
  2. 用户输入用户名/密码
  3. 认证/授权服务器验证凭证
  4. 认证/授权服务器显示授权页面(请求用户授权)
  5. 用户点击 "同意"授权

认证/授权服务器生成Authorization Code,并缓存code_challenge与Authorization Code的关联。

认证/授权服务器重定向

发出HTTP请求如下:

javascript 复制代码
HTTP/1.1 302 Found
Location: https://client.example.com/callback?
  code=SplxlOBeZQQYbYS6WxSbIA&
  state=af0ifjsldkj

返回一次性Authorization Code给客户端。

客户端换取令牌

发出HTTP请求如下:

javascript 复制代码
POST /token HTTP/1.1
Host: auth.server.com
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code&
code=SplxlOBeZQQYbYS6WxSbIA&
redirect_uri=https://client.example.com/callback&
client_id=s6BhdRkqt3&
code_verifier=dGhpcyBpcyBhIGNoYWxsZW5nZQ

使用一次性Authorization Code和code_verifier请求Token。

认证/授权服务器通过Authorization Code找到对应的code_challenge,然后验证code_verifier有效后,返回如下:

javascript 复制代码
{
  "access_token": "2YotnFZFEjr1zCsicMWpAA",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "tGzv3JOkF0XG5Qx2TlKWIA",
  "id_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."
}

其中ID Token是给客户端使用验证用户身份的,根据OpenID Connect规范的要求必须采用JWT格式。

Access Token是给资源服务器鉴定用户的访问权限的,用于请求资源服务器,可以是JWT,也可以是Reference Token。JWT是自包含的,鉴权时无需调用认证/授权服务器;Reference Token的鉴权需要调用认证/授权服务器。Keycloak默认使用Reference Token。JWT是无状态、自包含的,签发后无法主动失效,只能等待过期失效。Reference Token可以根据需要随时注销。

ID Token和Access Token的有效期都较短。

Refresh Token有效期较长,缓存在Redis中,用于生成新的Access Token。Refresh Token过期后,需要重新认证。

客户端验证 ID Token
javascript 复制代码
// verify ID Token
const payload = verifyJwt(idToken, {
  issuer: "https://auth.server.com",
  audience: "s6BhdRkqt3",
  nonce: storedNonce,
  jwksUri: "https://auth.server.com/oauth/jwks"
});

// save to session
sessionStorage.setItem("user_id", payload.sub);
sessionStorage.setItem("user_name", payload.name);
sessionStorage.setItem("access_token", accessToken);

客户端会缓存ID Token,在必要的时候(如敏感请求、用户重新登录等)随时检查ID Token是否过期。ID Token过期后,可以使用Refresh Token请求新的ID Token,此时用户无需登录。

客户端使用Access Token访问资源服务器的资源

发出HTTP请求如下:

javascript 复制代码
GET /api/user/profile HTTP/1.1
Host: api.example.com
Authorization: Bearer 2YotnFZFEjr1zCsicMWpAA
资源服务器向认证/授权服务器验证Access Token(仅Reference Token需要, JWT不需要)

JWT是自包含的,无需验证,但只能到期失效。Reference Token需要验证,但便于随时注销。

发出HTTP验证请求如下:

javascript 复制代码
POST /introspect HTTP/1.1
Host: auth.server.com
Authorization: Basic base64(api-client:secret)

token=2YotnFZFEjr1zCsicMWpAA

认证/授权服务器返回"active": true表示有效,示例如下:

javascript 复制代码
{
  "active": true,
  "scope": "profile email",
  "client_id": "s6BhdRkqt3",
  "username": "taiyangdao@example.com",
  "token_type": "Bearer",
  "exp": 1716123456,
  "iat": 1716120456,
  "sub": "123456789"
}
资源服务器看到有效Access Token,返回被请求的资源
javascript 复制代码
{
  "id": 123,
  "name": "taiyangdao",
  "email": "taiyangdao@example.com"
}
刷新Access Token

Access Token过期后,需要通过Refresh Token请求新的Access Token,HTTP请求如下:

javascript 复制代码
POST /token HTTP/1.1
Host: auth.server.com
Content-Type: application/x-www-form-urlencoded

grant_type=refresh_token&
refresh_token=tGzv3JOkF0XG5Qx2TlKWIA&
client_id=s6BhdRkqt3&
client_secret=secret

认证/授权服务器返回如下:

javascript 复制代码
{
  "access_token": "new_access_token_xyz",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "new_refresh_token_xyz"
}

每次使用Refresh Token,都会创建新的Refresh Token,但是其有效期仍然从最初认证时间计算。如果Refresh Token过期,则用户必须重新登录。

相关推荐
damo王1 天前
极简Agent plan指南
大模型·agent·token·向量模型·open claw·coding plan·agent plan
曲幽2 天前
你的Agent API还在裸奔?从认证到沙箱,我用FastAPI搭了几道防线
python·fastapi·web·security·jwt·oauth2·limit·sandbox·ai agent
无风听海2 天前
OAuth 中的state参数:被低估的安全基石
安全·oauth
无风听海3 天前
PKCE:从协议设计到安全实践的深度解析
oauth
小小工匠3 天前
Spring AI RAG - 08 JWT 认证与用户体系设计
spring·jwt
无风听海4 天前
OAuth 2.0 response_type完全指南
java·开发语言·oauth
晓翔仔4 天前
从零搭建自己的网站 AI 助手:阿里云百炼 + 云服务器部署全教程
服务器·人工智能·阿里云·token·ai助手
无风听海5 天前
OAuth 2.0 前端通道与后端通道深入剖析
前端·oauth
随缘而动,随遇而安5 天前
第九十八篇 工程落地视角:Session/Cookie/Token 原理辨析与大数据实战
大数据·spark·token·cookie·session