授权码模式
授权码模式(Authorization Code Flow)是OAuth 2.0和OpenID Connect(OIDC)中最安全、最常用的认证授权流程。本文档详细介绍Keycloak授权码模式的完整流程、参数配置和实现细节。
概述
授权码模式是一种重定向基础的流程,要求客户端能够与用户代理(通常是Web浏览器)交互,并且能够接收来自授权服务器的重定向请求。该模式特别适用于Web应用程序、移动应用和桌面应用。
核心特点
- 安全性高:客户端永远不会直接处理用户凭据
- 令牌交换:通过授权码换取访问令牌
- 支持刷新令牌:可以获取长期有效的刷新令牌
- OIDC兼容:支持获取ID令牌和用户信息
授权流程详解
Keycloak授权码模式包含以下几个关键步骤:
步骤1:构建授权请求
客户端将用户重定向到Keycloak的授权端点,携带必要的参数。
端点URL格式:
https://api.tongji.edu.cn/keycloak/realms/OpenPlatform/protocol/openid-connect/auth请求参数:
| 参数 | 必填 | 说明 | 示例值 |
|---|---|---|---|
response_type | 是 | 固定值code,表示使用授权码模式 | code |
client_id | 是 | 所分配的client_id | test_login |
redirect_uri | 是 | 授权完成后的回调地址 | https://dev.tongji.edu.cn/authorization-demo/ |
scope | 推荐 | 请求的权限范围 | openid |
state | 推荐 | 防CSRF攻击的随机值 | abc123 |
kc_idp_hint | 是 | 使用的第三方登录器,为了用户体验请填写固定值tjiam | tjiam |
完整示例:
https://api.tongji.edu.cn/keycloak/realms/OpenPlatform/protocol/openid-connect/auth?
response_type=code&
client_id=test_login&
redirect_uri=https://dev.tongji.edu.cn/authorization-demo/&
scope=openid&
state=34e17e87-8618-4d79-86db-136ca8400f25&
kc_idp_hint=tjiam步骤2:用户认证和授权
用户会被引导到开放平台的登录界面,完成认证以后会触发授权。
符合以下情况,则该授权页面不会弹出
- 用户已经授权
- 应用没有申请任何接口权限
- 应用被允许无需弹出授权页面
步骤3:返回授权码
用户授权成功后,Keycloak将用户重定向到客户端指定的redirect_uri,并携带以下参数:
成功响应参数:
| 参数 | 说明 |
|---|---|
code | 授权码,有效期很短 |
state | 原样返回客户端提供的state值 |
示例重定向URL:
https://dev.tongji.edu.cn/authorization-demo/callback?code=SplxlOBeZQQYbYS6WxSbIA&state=abc123错误响应参数:
| 参数 | 说明 |
|---|---|
error | 错误代码(如access_denied、temporarily_unavailable) |
error_description | 错误描述 |
state | 原样返回state值 |
步骤4:交换访问令牌
客户端使用收到的授权码向Keycloak令牌端点请求访问令牌。
端点URL:
https://api.tongji.edu.cn/v1/token请求方式: POST Content-Type: application/x-www-form-urlencoded
请求参数:
| 参数 | 必填 | 说明 |
|---|---|---|
grant_type | 是 | 固定值authorization_code |
client_id | 是 | 所分配的 client_id |
client_secret | 否 | 所分配的 client_secret(大多数客户端需要) |
code | 是 | 步骤3获得的授权码 |
redirect_uri | 是 | 必须与授权请求中的redirect_uri一致 |
示例请求:
curl -X POST \
https://api.tongji.edu.cn/v1/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=authorization_code" \
-d "client_id=my-client" \
-d "client_secret=your-secret" \
-d "code=SplxlOBeZQQYbYS6WxSbIA" \
-d "redirect_uri=https://dev.tongji.edu.cn/authorization-demo/"成功响应示例:
{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "Bearer",
"expires_in": 300,
"refresh_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"refresh_expires_in": 1800,
"id_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"scope": "openid profile email",
"session_state": "e48e5a57-86da-4cf5-8fc4-XXXXXX"
}成功响应参数:
| 参数名 | 备注 |
|---|---|
| access_token | 获得的token |
| expires_in | token的有效期 |
| token_type | token类型,现阶段只有Bearer类型Token |
| refresh_token | 用于刷新令牌 |
| refresh_expires_in | refresh_token的有效期 |
| scope | 申请的授权范围 |
| not-before-policy | 现阶段无需关注 |
| id_token | 现阶段无需关注 |
步骤5:使用访问令牌
获取访问令牌后,客户端再进一步获取用户信息。例如通过解析 id_token 获取用户名。或者请求具体的用户信息接口即可。
API请求示例:
curl -X GET \
https://api.tongji.edu.cn/v1/dc/user/single_info \
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."令牌类型说明
访问令牌(Access Token)
- 用途:访问受保护的API资源
- 格式:JWT token
- 有效期:默认2h,可申请配置
- 验证:资源服务器需要验证令牌的有效性
ID令牌(ID Token)
- 用途:包含用户身份信息
- 格式:JWT
- 包含信息:用户标识、认证时间、发行者等
- 验证:客户端需要验证签名和声明
刷新令牌(Refresh Token)
- 用途:获取新的访问令牌
- 有效期:默认半年,可申请配置
- 存储:需要安全存储
- 使用:当访问令牌过期时使用
注意事项
授权服务器提供 access_token 的令牌签发,并确保令牌签发的过程中有用户的认证授权确认。会话应由应用端自行维护管理。尽管我们在授权码模式中,集成了 CAS 认证协议,并与学校其他 CAS 集成应用相兼容并支持 SSO。但 SSO 的会话仍然应该与应用本身的会话相独立,已经完成认证的用户应该由应用本身会话管理,避免不必要的认证触发行为。
当应用的会话有效期比较长时,可能需要刷新 token 以重新获取用户信息,此时可以使用 refresh_token 来刷新 token。
刷新令牌
当访问令牌过期时,可以使用刷新令牌获取新的访问令牌。
请求示例:
curl -X POST \
https://api.tongji.edu.cn/v1/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=refresh_token" \
-d "client_id=my-client" \
-d "client_secret=your-secret" \
-d "refresh_token=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."响应示例:
{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "Bearer",
"expires_in": 300,
"refresh_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"refresh_expires_in": 1800,
"id_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"session_state": "e48e5a57-86da-4cf5-8fc4-XXXXXX"
}获取用户信息
使用访问令牌调用用户信息端点获取用户详细信息。
端点URL:
https://api.tongji.edu.cn/keycloak/realms/OpenPlatform/protocol/openid-connect/userinfo请求示例:
curl -X GET \
https://api.tongji.edu.cn/keycloak/realms/OpenPlatform/protocol/openid-connect/userinfo \
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."响应示例:
{
"sub": "1234567890",
"name": "John Doe",
"preferred_username": "johndoe",
"given_name": "John",
"family_name": "Doe",
"userid": "xxxx"
}登出
需要同时登出keycloak与同济大学统一身份认证(tjiam)
keycloak登出
重定向用户到登出端点:
https://api.tongji.edu.cn/keycloak/realms/OpenPlatform/protocol/openid-connect/logout?
redirect_uri=https://dev.tongji.edu.cn/authorization-demo/同济大学统一身份认证(tjiam)登出
使用刷新令牌进行后端登出:
https://iam.tongji.edu.cn/idp/profile/OAUTH2/Redirect/GLO?
redirectToUrl=https://dev.tongji.edu.cn/authorization-demo&
entity=client_id错误处理
常见错误代码
| 错误代码 | 描述 | 解决方案 |
|---|---|---|
invalid_request | 请求缺少必需参数或参数无效 | 检查请求参数格式和完整性 |
unauthorized_client | 客户端未授权使用此授权类型 | 检查客户端配置 |
access_denied | 用户或授权服务器拒绝了请求 | 提示用户重新授权 |
unsupported_response_type | 不支持的响应类型 | 使用正确的response_type |
invalid_scope | 请求的作用域无效 | 使用有效的作用域 |
server_error | 授权服务器内部错误 | 重试或联系管理员 |
temporarily_unavailable | 服务暂时不可用 | 稍后重试 |
错误响应格式
重定向错误响应:
https://dev.tongji.edu.cn/authorization-demo/callback?
error=access_denied&
error_description=User%20denied%20the%20request&
state=abc123令牌端点错误响应:
{
"error": "invalid_grant",
"error_description": "Invalid authorization code"
}