言简意赅的讲解Keycloak OIDC 与 OAuth 2.0解决的痛点
Keycloak 是一款开源的身份和访问管理(IAM)工具,支持多种协议,包括 OIDC(OpenID Connect)、OAuth 2.0、SAML 等。它简化了身份认证与授权流程,提供了集中管理用户、角色、权限等功能,常被用作企业或微服务体系下的统一认证中心。
之前给大家讲解了Keycloak一键登录,后续就有同学发现好像还是不能实现真正SSO。其实真的要实现SSO还是稍微复杂些,本文给大家讲讲OAuth实现的真正的SSO。
1.1 OIDC 与 OAuth 的关系
- OAuth 2.0:一个授权框架,主要关注“授权”的过程,让第三方应用有权限访问用户在某一服务上的受保护资源。
- OIDC(OpenID Connect):是基于 OAuth 2.0 的一个身份层(Identity Layer)。也就是说,OIDC 除了完成 OAuth 2.0 的授权能力之外,还额外提供了对用户进行“身份认证”的能力。OIDC 可以让应用获取到更丰富的用户身份信息(如
id_token
中包含的用户信息)。
1.2 为什么 OIDC 登录无法与 OAuth 登录混用
当你在 Keycloak 中配置了 OIDC 流程时,你获得的不仅仅是访问资源的授权,还包含了用户身份信息的校验(id_token
等)。如果你在另一个系统中使用 纯 OAuth 登录,那么该系统关心的更多是令牌用于访问资源的权限验证,可能并不关心用户的具体身份信息格式(或者通过自定义方式来获取),因此它不会自动识别你在 Keycloak 端(OIDC)的认证信息,这就导致需要再次输入用户名和密码进行登录。
常见场景例如:
- 你在 Keycloak 中使用 OIDC 流程拿到了
id_token
和access_token
。 - 访问另一个需要 OAuth 2.0 纯授权逻辑的第三方系统时,该系统只接受 OAuth 2.0 标准流程颁发的
access_token
(或自家的登录态),由于它无法识别并信任 Keycloak “外部”签发的 token(除非做额外的信任适配),因此会要求再次登录。
二、常用 OAuth 连接参数及示例
以 Authorization Code Flow(授权码模式)为例,常见的请求示例及参数说明如下:
2.1 获取授权码的请求
HTTP 方法:GET
示例 URL(Keycloak 为例):
https://<KEYCLOAK-HOST>/realms/<REALM-NAME>/protocol/openid-connect/auth
?client_id=<CLIENT_ID>
&redirect_uri=<REDIRECT_URI>
&response_type=code
&scope=openid profile email
&state=<RANDOM_STATE>
client_id
:应用的客户端 ID,在 Keycloak 上注册后获得。redirect_uri
:回调地址,用于在授权成功后返回给应用。response_type
:典型设置为code
表示使用授权码模式。scope
:权限范围,比如openid
(标识使用 OIDC),profile
(基本资料),email
(获取邮件地址)等。state
:防止 CSRF 攻击的随机字符串,拿到授权码后会原样返回给客户端。
Keycloak oauth2.0 链接参数样例
2.2 根据授权码获取 Access Token
当用户在 Keycloak 端成功授权后,会将页面重定向回 redirect_uri
并携带 code
和 state
等参数。然后客户端使用该 code
再向 Keycloak 请求 Access Token:
HTTP 方法:POST
URL:
https://<KEYCLOAK-HOST>/realms/<REALM-NAME>/protocol/openid-connect/token
请求体(application/x-www-form-urlencoded):
client_id=<CLIENT_ID>
client_secret=<CLIENT_SECRET>
grant_type=authorization_code
code=<AUTHORIZATION_CODE>
redirect_uri=<REDIRECT_URI>
client_secret
:客户端密钥,机密客户端(Confidential Client)在 Keycloak 上配置生成。grant_type
:使用authorization_code
表示授权码模式。code
:上一步得到的授权码。redirect_uri
:与申请授权码时的redirect_uri
必须一致。
返回示例(JSON 格式):
{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.XXXXXX",
"expires_in": 300,
"refresh_expires_in": 1800,
"refresh_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.YYYYYY",
"token_type": "Bearer",
"id_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.ZZZZZZ",
"not-before-policy": 0,
"session_state": "e48e5a57-86da-4cf5-8fc4-XXXXXX",
"scope": "openid profile email"
}
2.3 刷新 Token
HTTP 方法:POST
URL:
https://<KEYCLOAK-HOST>/realms/<REALM-NAME>/protocol/openid-connect/token
请求体:
client_id=<CLIENT_ID>
client_secret=<CLIENT_SECRET>
grant_type=refresh_token
refresh_token=<REFRESH_TOKEN>
grant_type
:此处使用refresh_token
。refresh_token
:从上一步获取到的refresh_token
。
返回示例与获取 Access Token 类似,主要区别在于新的 access_token
、refresh_token
会更新。
三、使用接口形式讲解 OAuth 登录
在一些场景下,我们会直接与 Keycloak 的接口进行交互,而不是仅通过浏览器跳转。最常见的就是后端服务或移动端 App 需要对接 Keycloak。以下列举常见的 授权码模式 与 客户端模式 接口交互。
3.1 授权码模式(Authorization Code Flow)接口交互
前提:你的应用或后端已经能够接收浏览器重定向回来的 code
。
-
用户访问前端或后端需要资源
- 如果后端判断当前用户没有携带有效的
access_token
,则引导用户浏览器跳转到 Keycloak 的授权地址,并带上client_id
、redirect_uri
、scope
等参数。
- 如果后端判断当前用户没有携带有效的
-
用户在 Keycloak 登录
- Keycloak 会展示登录界面,用户输入用户名密码进行验证。
-
Keycloak 重定向回客户端
- Keycloak 认证通过后,将浏览器重定向回
redirect_uri
,并附带code
、state
。
- Keycloak 认证通过后,将浏览器重定向回
-
后端(或前端)使用
code
换取令牌- 通过
POST
请求到 Keycloak 的token
接口(上文 2.2)获取access_token
、refresh_token
、id_token
等。
- 通过
-
后续访问受保护资源
- 前端或后端在后续请求中将
access_token
放在Authorization: Bearer <ACCESS_TOKEN>
请求头中即可访问受保护的 API。
- 前端或后端在后续请求中将
3.2 客户端模式(Client Credentials Flow)接口交互
这种场景下,通常是服务端对服务端的交互,不涉及用户登录界面。例如:一个微服务 A 调用 Keycloak 获取 token,然后使用该 token 去访问另一个受 Keycloak 保护的微服务 B。
-
配置 Client Credentials
- 在 Keycloak 中,为相应的客户端分配
client_id
和client_secret
。 - 授予该客户端调用目标资源所需的访问范围(Roles/Scope)。
- 在 Keycloak 中,为相应的客户端分配
-
获取 Access Token
-
直接通过后端的脚本或代码向 Keycloak 发送
POST
请求:POST https://<KEYCLOAK-HOST>/realms/<REALM-NAME>/protocol/openid-connect/token Content-Type: application/x-www-form-urlencoded grant_type=client_credentials client_id=<CLIENT_ID> client_secret=<CLIENT_SECRET>
-
-
使用 Access Token 访问目标资源
- 将响应中的
access_token
放入请求头Authorization: Bearer <ACCESS_TOKEN>
,即可访问受保护的资源或 API。
- 将响应中的
四、Keycloak 详细示例:从获取 Token 到查询用户信息
以下以 Keycloak 上的 OIDC 为例,展示如何在拿到 access_token
之后,进一步使用 Token 来查询用户信息等操作。
4.1 获取 Token
参照上文 2.2 的方式,我们在拿到授权码后,通过接口向 Keycloak 交换得到以下令牌:
{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5...",
"refresh_token": "eyJhbGciOiJSUzI1NiIsInR5...",
"id_token": "eyJhbGciOiJSUzI1NiIsInR5..."
}
4.2 查询用户信息(User Info Endpoint)
OIDC 规定提供 /userinfo
端点来获取用户的基础信息。在 Keycloak 中,该端点一般如下:
GET https://<KEYCLOAK-HOST>/realms/<REALM-NAME>/protocol/openid-connect/userinfo
Authorization: Bearer <ACCESS_TOKEN>
示例返回:
{
"sub": "245bb7ae-73fc-4def-9d7f-xxxxxx",
"email_verified": true,
"name": "John Doe",
"preferred_username": "johndoe",
"given_name": "John",
"family_name": "Doe",
"email": "john.doe@example.com"
}
4.3 获取用户角色信息
如果你在 Keycloak 中给用户分配了角色(Roles),可在令牌或 /userinfo
接口中获取。如果想要直接在 Access Token 中查看角色信息,需要在 Keycloak 中的 Client Scopes
、Mapper
中配置将角色信息映射到令牌中。
4.4 验证 Token
在后端要验证 Token 时,可以使用 Keycloak 提供的公钥或使用 Keycloak 官方的适配器(Keycloak Adapter)。验证的核心点包括:
- Token 是否在有效期内(过期时间
exp
)。 - Token 的签名是否正确(需要使用 Keycloak 公钥解密或使用发行者的 JWKS URL 验证)。
- Token 的
aud
(受众)是否与你的应用client_id
相匹配。 - Token 的
iss
(发行者)是否是你的 Keycloak 实例地址。
五、更多用法
-
单点登出(Single Logout)
- 当用户在客户端进行 Logout 时,可调用 Keycloak 的 Logout 接口,注销全局 Session。
- URL 通常是:
GET https://<KEYCLOAK-HOST>/realms/<REALM-NAME>/protocol/openid-connect/logout ?id_token_hint=<ID_TOKEN> &post_logout_redirect_uri=<REDIRECT_URI>
-
自定义用户属性
- 可以在 Keycloak 中给用户添加自定义属性,通过
Mapper
机制映射到id_token
或access_token
中,或者通过/userinfo
获取。
- 可以在 Keycloak 中给用户添加自定义属性,通过
-
多身份源(Identity Brokering)
- Keycloak 不仅支持本地用户,还能通过身份代理的方式引入其他 IdP(如 GitHub、Google、微软等),但这需要在 Keycloak 端做配置。
- 如果目标系统只认 OAuth 2.0(而非 OIDC),则仍需额外的映射和适配,否则就会出现需要再次登录的情况。
六、总结
-
Keycloak 通过支持 OIDC 和 OAuth 2.0,为应用和服务提供了简便的身份认证与授权方案,但要注意 OIDC 和 OAuth 两种场景是有差异的:OIDC 包含身份信息,OAuth 主要面向授权。如同开篇提到的,一旦在 Keycloak 上进行了 OIDC 登录,在一个只支持纯 OAuth 的系统里通常无法复用该登录态,需要再次输入用户名密码。
-
典型的 OAuth 连接参数包括
client_id
、redirect_uri
、response_type
、scope
、state
等,这些参数决定了授权流程的行为。 -
要通过接口来完成 OAuth 登录,需要先触发授权请求(获取
code
),再使用code
请求access_token
,进而拿到id_token
等信息后即可进行后续与 Keycloak 的交互,比如获取用户信息、刷新 token 或单点注销等操作。 -
Keycloak 示例 提供了详细的
/token
、/userinfo
接口,可演示从零到一如何完成整个认证授权过程,同时可扩展到更多高级功能,如用户角色映射、自定义属性等。
从企业应用到微服务架构,Keycloak 都能作为统一的身份管理中心。当你掌握了 Keycloak 与 OIDC/OAuth 交互的基本模式,也就能够进一步拓展至更多复杂场景,比如多方身份提供商接入、单点登录、单点登出、高级授权策略(基于角色、基于资源服务器的访问控制)等。
希望本篇内容能够帮助你更好地理解 Keycloak 在 OIDC 与 OAuth 领域的应用场景,并为你的应用或服务提供一个更安全、可扩展的用户认证及授权体系。
通过上述内容,你就已经基本理解了这个方法,基础用法我也都有展示。如果你能融会贯通,我相信你会很强
Best
Wenhao (楠博万)