OpenID Connect的认证与授权详解
OAuth 2.0是一种授权机制,用户通过授权服务器给第三方应用授权,以访问自己的资源(位于资源服务器)。相对于OAuth 1.0,OAuth 2.0简化了对客户端的验证,只保留了获取Authorization Code和Access Token的请求过程。
OpenID Connect (OIDC)是OAuth 2.0的一个扩展,结合OAuth 2.0的授权提供了用户的认证。
常见的认证方式
基于Password:
- LDAP, 基于目录的认证, 如Active Directory
- RADIUS, 网络访问认证,如VPNs, Wi-Fi
基于Certificate / Key:
- mTLS (Mutual TLS), 客户端/服务端双向X.509证书认证
- SSH public key, 非对称加密的远程访问认证
基于Token / Web:
- OAuth 2.0
- OpenID Connect
- JWT (JSON Web Token)
- SAML 2.0
基于Challenge-Response:
- Kerberos, 基于Ticket的双向认证,是Windows domain认证的核心
- Digest Auth, 使用Hash身份的HTTP challenge-response认证
无密码:
- FIDO2 / WebAuthn, 使用硬件Key或生物信息的抗钓鱼的认证
- Passkeys, 基于FIDO2,通过Apple/Google/Microsoft等平台在终端之间同步的认证
多要素:
- TOTP (RFC 6238), 基于Time的一次性密码,如Authenticator
- HOTP (RFC 4226), 基于Counter的OTP
其中,常见的SSO认证方式有OIDC, SAML 2.0, Kerberos。
常见的Token格式
- JWT(JSON Web Token)
- Reference Token (随机字符串)
- SAML Token(XML 格式)
- MAC Token(OAuth 1.0专用,已废弃)
结合Authorization Code + PKCE的OIDC认证与授权
PKCE (Proof Key for Code Exchange),通过 code_verifier 和 code_challenge 确保只有原始客户端能获得Access Token。
客户端准备
// 客户端生成 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请求如下:
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
认证/授权服务器对用户进行认证/授权
- 认证/授权服务器显示登录页面
- 用户输入用户名/密码
- 认证/授权服务器验证凭证
- 认证/授权服务器显示授权页面(请求用户授权)
- 用户点击 "同意"授权
认证/授权服务器生成Authorization Code,并缓存code_challenge与Authorization Code的关联。
认证/授权服务器重定向
发出HTTP请求如下:
HTTP/1.1 302 Found
Location: https://client.example.com/callback?
code=SplxlOBeZQQYbYS6WxSbIA&
state=af0ifjsldkj
返回一次性Authorization Code给客户端。
客户端换取令牌
发出HTTP请求如下:
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有效后,返回如下:
{
"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
// 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请求如下:
GET /api/user/profile HTTP/1.1
Host: api.example.com
Authorization: Bearer 2YotnFZFEjr1zCsicMWpAA
资源服务器向认证/授权服务器验证Access Token(仅Reference Token需要, JWT不需要)
JWT是自包含的,无需验证,但只能到期失效。Reference Token需要验证,但便于随时注销。
发出HTTP验证请求如下:
POST /introspect HTTP/1.1
Host: auth.server.com
Authorization: Basic base64(api-client:secret)
token=2YotnFZFEjr1zCsicMWpAA
认证/授权服务器返回"active": true表示有效,示例如下:
{
"active": true,
"scope": "profile email",
"client_id": "s6BhdRkqt3",
"username": "taiyangdao@example.com",
"token_type": "Bearer",
"exp": 1716123456,
"iat": 1716120456,
"sub": "123456789"
}
资源服务器看到有效Access Token,返回被请求的资源
{
"id": 123,
"name": "taiyangdao",
"email": "taiyangdao@example.com"
}
刷新Access Token
Access Token过期后,需要通过Refresh Token请求新的Access Token,HTTP请求如下:
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
认证/授权服务器返回如下:
{
"access_token": "new_access_token_xyz",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "new_refresh_token_xyz"
}
每次使用Refresh Token,都会创建新的Refresh Token,但是其有效期仍然从最初认证时间计算。如果Refresh Token过期,则用户必须重新登录。
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐


所有评论(0)