设置 Keycloak 单点登录
OpenID Connect (OIDC) 是位于 OAuth 2.0 协议 之上的简单身份层。它允许客户端根据身份提供商执行的身份验证来验证最终用户的身份,并以可互操作和类似 REST 的方式获取有关最终用户的基本个人资料信息。通过 APISIX 和 Keycloak,你可以实施基于 OIDC 的身份验证流程来保护你的 API 并启用单点登录 (SSO)。
Keycloak 是针对现代应用程序和服务的开源身份和访问管理解决方案。Keycloak 支持单点登录,使服务能够通过 OIDC 和 OAuth 2.0 等协议与 Keycloak 连接。此外,Keycloak 还支持将身份验证委托给第三方身份提供商,如 Facebook 和 Google。
本指南将向你展示如何使用 openid-connect 插件,通过 授权码授予、客户端凭证授予 和 密码授予 将 APISIX 与 Keycloak 集成。

前置条件
配置 Keycloak
以 开发模式 启动一个名为 apisix-quickstart-keycloak 的 Keycloak 实例,管理员名称为 quickstart-admin,密码为 quickstart-admin-pass:
- Docker
- Kubernetes
docker run -d --name "apisix-quickstart-keycloak" \
-e 'KEYCLOAK_ADMIN=quickstart-admin' \
-e 'KEYCLOAK_ADMIN_PASSWORD=quickstart-admin-pass' \
-p 8080:8080 \
quay.io/keycloak/keycloak:18.0.2 start-dev
Keycloak 提供了一个易于使用的 Web UI,帮助管理员管理所有资源,如客户端、角色和用户。
为部署 Keycloak 创建一个 Kubernetes 清单文件:
apiVersion: apps/v1
kind: Deployment
metadata:
namespace: ingress-apisix
name: apisix-quickstart-keycloak
labels:
app: keycloak
spec:
replicas: 1
selector:
matchLabels:
app: keycloak
template:
metadata:
namespace: ingress-apisix
labels:
app: keycloak
spec:
containers:
- name: keycloak
image: quay.io/keycloak/keycloak:18.0.2
args: ["start-dev"]
env:
- name: KEYCLOAK_ADMIN
value: "quickstart-admin"
- name: KEYCLOAK_ADMIN_PASSWORD
value: "quickstart-admin-pass"
ports:
- containerPort: 8080
为 Keycloak 服务创建另一个 Kubernetes 清单文件:
apiVersion: v1
kind: Service
metadata:
namespace: ingress-apisix
name: keycloak
spec:
selector:
app: keycloak
ports:
- protocol: TCP
port: 8080
targetPort: 8080
nodePort: 32080
type: NodePort
将配置应用到你的集群:
kubectl apply -f keycloak-deployment.yaml -f keycloak-service.yaml
通过端口转发将 Keycloak 服务端口暴露到本地计算机:
kubectl port-forward svc/keycloak 8080:8080 &
在浏览器中导航到 http://localhost:8080 以访问 Keycloak 网页,然后单击 Administration Console:

输入管理员的用户名 quickstart-admin 和密码 quickstart-admin-pass 并登录:

你需要保持登录状态以在后续步骤中配置 Keycloak。
创建 Realm
Keycloak 中的 Realm 是管理用户、凭证和角色等资源的工作区。不同 realm 中的资源相互隔离。你需要为 APISIX 创建一个名为 quickstart-realm 的 realm。
在左侧菜单中,将鼠标悬停在 Master 上,然后在下拉菜单中选择 Add realm:

输入 realm 名称 quickstart-realm,然后单击 Create 创建它:

创建 Client
Keycloak 中的 Client 是请求 Keycloak 对用户进行身份验证的实体。通常,客户端是希望使用 Keycloak 保护自身并提供单点登录解决方案的应用程序。APISIX 相当于一个负责向 Keycloak 发起身份验证请求的客户端,因此你需要创建一个名为 apisix-quickstart-client 的相应客户端。
单击 Clients > Create 打开 Add Client 页面:

输入 Client ID 为 apisix-quickstart-client,然后选择 Client Protocol 为 openid-connect 并 Save:

客户端 apisix-quickstart-client 已创建。重定向到详细页面后,选择 confidential 作为 Access Type:

在单点登录期间用户登录成功时,Keycloak 将携带状态和代码将客户端重定向到 Valid Redirect URIs 中的地址。为了简化操作,输入通配符 * 以认为任何 URI 有效:

如果你正在实施 带有 PKCE 的授权码授予,请在客户端的高级设置中配置 PKCE 质询方法:

如果你正在实施 客户端凭证授予,请为客户端启用服务帐户:

选择 Save 以应用自定义配置。
创建用户
Keycloak 中的用户是能够登录系统的实体。他们可以拥有与之关联的属性,例如用户名、电子邮件和地址。
如果你只实施 客户端凭证授予,则可以 [跳过此部分](#获取 OIDC 配置)。
单击 Users > Add user 打开 Add user 页面:

输入 Username 为 quickstart-user,然后选择 Save:

单击 Credentials,然后将 Password 设置为 quickstart-user-pass。将 Temporary 切换为 OFF 以关闭限制,这样你在第一次登录时无需更改密码:

获取 OIDC 配置
在本节中,你将从 Keycloak 获取关键 OIDC 配置并将其定义为 shell 变量。本节之后的步骤将使用这些变量通过 shell 命令配置 OIDC。
打开一个单独的终端来执行步骤并定义相关的 shell 变量。本节之后的步骤可以直接使用定义的变量。
获取发现端点
单击 Realm Settings,然后右键单击 OpenID Endpoints Configuration 并复制链接。

该链接应与以下内容相同:
http://localhost:8080/realms/quickstart-realm/.well-known/openid-configuration
OIDC 身份验证期间需要此端点公开的配置值。
- Docker
- Kubernetes
使用你的主机 IP 更新地址并保存到环境变量:
export KEYCLOAK_IP=192.168.42.145 # 替换为你的主机 IP
export OIDC_DISCOVERY=http://${KEYCLOAK_IP}:8080/realms/quickstart-realm/.well-known/openid-configuration
获取 Kubernetes 节点 IP:
kubectl get nodes -o wide
你应该在响应中找到你的节点 IP:
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
kind-control-plane Ready control-plane 47h v1.30.0 192.168.117.2 <none> Debian GNU/Linux 12 (bookworm) 6.14.10-orbstack-00291-g1b252bd3edea containerd://1.7.15
你的 OIDC 发现 URL 应使用你的 Kubernetes 节点 IP,后跟 NodePort:
http://192.168.117.2:32080/realms/quickstart-realm/.well-known/openid-configuration
获取客户端 ID 和 Secret
单击 Clients > apisix-quickstart-client > Credentials,并从 Secret 复制客户端 secret:


将 OIDC 客户端 ID 和 secret 保存到环境变量:
export OIDC_CLIENT_ID=apisix-quickstart-client
export OIDC_CLIENT_SECRET=bSaIN3MV1YynmtXvU8lKkfeY0iwpr9cH # 替换为你的值
实施授权码授予
授权码授予用于 Web 和移动应用程序。该流程从授权服务器在浏览器中显示登录页面开始,用户可以在其中输入凭证。在此过程中,短期授权码将交换为访问令牌,APISIX 将其存储在浏览器会话 cookie 中,并将随访问上游资源服务器的每个请求一起发送。
要实施授权码授予,请创建一个带有 openid-connect 插件的路由,如下所示:
- Admin API
- ADC
- Ingress Controller
curl -i "http://127.0.0.1:9180/apisix/admin/routes" -X PUT -d '
{
"id": "auth-with-oidc",
"uri":"/anything/*",
"plugins": {
"openid-connect": {
"bearer_only": false,
"session": {
"secret": "f86cf31663a9c9fa0a28c2cc78badef1"
},
"client_id": "'"$OIDC_CLIENT_ID"'",
"client_secret": "'"$OIDC_CLIENT_SECRET"'",
"discovery": "'"$OIDC_DISCOVERY"'",
"scope": "openid profile",
"redirect_uri": "http://localhost:9080/anything/callback"
}
},
"upstream":{
"type":"roundrobin",
"nodes":{
"httpbin.org:80":1
}
}
}'
❶ bearer_only:对于授权码授予,设置为 false。
❷ session.secret:替换为你用于会话加密和 HMAC 操作的密钥。当 bearer_only 为 false 时为必需。
❸ client_id:Keycloak 客户端 ID。
❹ client_secret:Keycloak 客户端 secret。
❺ discovery:发现文档的 URI。
❺ redirect_uri:使用 Keycloak 身份验证后重定向到的 URI。参见 重定向 URI。
services:
- name: httpbin Service
routes:
- uris:
- /anything/*
name: auth-with-oidc
plugins:
openid-connect:
bearer_only: false
session:
secret: "f86cf31663a9c9fa0a28c2cc78badef1"
client_id: "apisix-quickstart-client"
client_secret: "bSaIN3MV1YynmtXvU8lKkfeY0iwpr9cH"
discovery: "http://192.168.42.145:8080/realms/quickstart-realm/.well-known/openid-configuration"
scope: openid profile
redirect_uri: "http://localhost:9080/anything/callback"
upstream:
type: roundrobin
nodes:
- host: httpbin.org
port: 80
weight: 1
❶ bearer_only:对于授权码授予,设置为 false。
❷ session.secret:替换为你用于会话加密和 HMAC 操作的密钥。当 bearer_only 为 false 时为必需。
❸ client_id:Keycloak 客户端 ID。
❹ client_secret:Keycloak 客户端 secret。
❺ discovery:发现文档的 URI。
❺ redirect_uri:使用 Keycloak 身份验证后重定向到的 URI。参见 重定向 URI。
将配置同步到 APISIX:
adc sync -f adc.yaml
- Gateway API
- APISIX CRD
apiVersion: apisix.apache.org/v1alpha1
kind: PluginConfig
metadata:
namespace: ingress-apisix
name: auth-plugin-config
spec:
plugins:
- name: openid-connect
config:
bearer_only: false
session:
secret: f86cf31663a9c9fa0a28c2cc78badef1
client_id: apisix-quickstart-client
client_secret: bSaIN3MV1YynmtXvU8lKkfeY0iwpr9cH
discovery: http://192.168.117.2:32080/realms/quickstart-realm/.well-known/openid-configuration
scope: openid profile
redirect_uri: http://localhost:9080/anything/callback
---
apiVersion: v1
kind: Service
metadata:
namespace: ingress-apisix
name: httpbin-external-domain
spec:
type: ExternalName
externalName: httpbin.org
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
namespace: ingress-apisix
name: auth-with-oidc
spec:
parentRefs:
- name: apisix
rules:
- matches:
- path:
type: PathPrefix
value: /anything/*
filters:
- type: ExtensionRef
extensionRef:
group: apisix.apache.org
kind: PluginConfig
name: auth-plugin-config
backendRefs:
- name: httpbin-external-domain
port: 80
apiVersion: apisix.apache.org/v2
kind: ApisixUpstream
metadata:
namespace: ingress-apisix
name: httpbin-external-domain
spec:
externalNodes:
- type: Domain
name: httpbin.org
---
apiVersion: apisix.apache.org/v2
kind: ApisixRoute
metadata:
namespace: ingress-apisix
name: auth-with-oidc
spec:
ingressClassName: apisix
http:
- name: auth-with-oidc
match:
paths:
- /anything/*
plugins:
- name: openid-connect
enable: true
config:
bearer_only: false
session:
secret: f86cf31663a9c9fa0a28c2cc78badef1
client_id: apisix-quickstart-client
client_secret: bSaIN3MV1YynmtXvU8lKkfeY0iwpr9cH
discovery: http://192.168.117.2:32080/realms/quickstart-realm/.well-known/openid-configuration
scope: openid profile
redirect_uri: http://localhost:9080/anything/callback
upstreams:
- name: httpbin-external-domain
❶ bearer_only:对于授权码授予,设置为 false。
❷ session.secret:替换为你用于会话加密和 HMAC 操作的密钥。当 bearer_only 为 false 时为必需。
❸ client_id:Keycloak 客户端 ID。
❹ client_secret:Keycloak 客户 端 secret。
❺ discovery:发现文档的 URI。
❺ redirect_uri:使用 Keycloak 身份验证后重定向到的 URI。参见 重定向 URI。
将配置应用到你的集群:
kubectl apply -f oidc-route.yaml
或者,如果你想实施带有 PKCE 的授权码授予,请创建一个带有 openid-connect 插件的路由,类似于上一个示例,但启用 use_pkce 选项:
- Admin API
- ADC
- Ingress Controller
curl -i "http://127.0.0.1:9180/apisix/admin/routes" -X PUT -d '
{
"id": "auth-with-oidc",
"uri":"/anything/*",
"plugins": {
"openid-connect": {
"bearer_only": false,
"session": {
"secret": "f86cf31663a9c9fa0a28c2cc78badef1"
},
"use_pkce": true,
"client_id": "'"$OIDC_CLIENT_ID"'",
"client_secret": "'"$OIDC_CLIENT_SECRET"'",
"discovery": "'"$OIDC_DISCOVERY"'",
"scope": "openid profile",
"redirect_uri": "http://localhost:9080/anything/callback"
}
},
"upstream":{
"type":"roundrobin",
"nodes":{
"httpbin.org:80":1
}
}
}'
❶ use_pkce:设置为 true 以启用 PKCE。
services:
- name: httpbin Service
routes:
- uris:
- /anything/*
name: auth-with-oidc
plugins:
openid-connect:
bearer_only: false
session:
secret: "f86cf31663a9c9fa0a28c2cc78badef1"
use_pkce: true
client_id: "apisix-quickstart-client"
client_secret: "bSaIN3MV1YynmtXvU8lKkfeY0iwpr9cH"
discovery: "http://192.168.42.145:8080/realms/quickstart-realm/.well-known/openid-configuration"
scope: openid profile
redirect_uri: "http://localhost:9080/anything/callback"
upstream:
type: roundrobin
nodes:
- host: httpbin.org
port: 80
weight: 1
❶ use_pkce:设置为 true 以启用 PKCE。
将配置同步到 APISIX:
adc sync -f adc.yaml
- Gateway API
- APISIX CRD
apiVersion: apisix.apache.org/v1alpha1
kind: PluginConfig
metadata:
namespace: ingress-apisix
name: auth-plugin-config
spec:
plugins:
- name: openid-connect
config:
bearer_only: false
session:
secret: f86cf31663a9c9fa0a28c2cc78badef1
use_pkce: true
client_id: apisix-quickstart-client
client_secret: bSaIN3MV1YynmtXvU8lKkfeY0iwpr9cH
discovery: http://192.168.117.2:32080/realms/quickstart-realm/.well-known/openid-configuration
scope: openid profile
redirect_uri: http://localhost:9080/anything/callback
---
apiVersion: v1
kind: Service
metadata:
namespace: ingress-apisix
name: httpbin-external-domain
spec:
type: ExternalName
externalName: httpbin.org
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
namespace: ingress-apisix
name: auth-with-oidc
spec:
parentRefs:
- name: apisix
rules:
- matches:
- path:
type: PathPrefix
value: /anything/*
filters:
- type: ExtensionRef
extensionRef:
group: apisix.apache.org
kind: PluginConfig
name: auth-plugin-config
backendRefs:
- name: httpbin-external-domain
port: 80
apiVersion: apisix.apache.org/v2
kind: ApisixUpstream
metadata:
namespace: ingress-apisix
name: httpbin-external-domain
spec:
externalNodes:
- type: Domain
name: httpbin.org
---
apiVersion: apisix.apache.org/v2
kind: ApisixRoute
metadata:
namespace: ingress-apisix
name: auth-with-oidc
spec:
ingressClassName: apisix
http:
- name: auth-with-oidc
match:
paths:
- /anything/*
plugins:
- name: openid-connect
enable: true
config:
bearer_only: false
session:
secret: f86cf31663a9c9fa0a28c2cc78badef1
use_pkce: true
client_id: apisix-quickstart-client
client_secret: bSaIN3MV1YynmtXvU8lKkfeY0iwpr9cH
discovery: http://192.168.117.2:32080/realms/quickstart-realm/.well-known/openid-configuration
scope: openid profile
redirect_uri: http://localhost:9080/anything/callback
upstreams:
- name: httpbin-external-domain
❶ use_pkce:设置为 true 以启用 PKCE。
将配置应用到你的集群:
kubectl apply -f oidc-route.yaml
使用有效凭证验证
在浏览器中导航到 http://localhost:9080/anything/test。请求将被重定向到登录页面:

使用正确的用户名 quickstart-user 和密码 quickstart-user-pass 登录。如果成功,请求将被转发到 httpbin.org,你应该看到类似以下的响应:
{
"args": {},
"data": "",
"files": {},
"form": {},
"headers": {
"Accept": "text/html..."
...
},
"json": null,
"method": "GET",
"origin": "127.0.0.1, 59.71.244.81",
"url": "http://127.0.0.1/anything/test"
}
使用无效凭证验证
使用错误的凭证登录。你应该看到身份验证失败:

实施客户端凭证授予
在客户端凭证授予中,客户端在没有任何用户参与的情况下 获取访问令牌。它通常用于机器对机器 (M2M) 通信。
要实施客户端凭证授予,请创建一个带有 openid-connect 插件的路由,如下所示:
- Admin API
- ADC
- Ingress Controller
curl -i "http://127.0.0.1:9180/apisix/admin/routes" -X PUT -d '
{
"id": "auth-with-oidc",
"uri":"/anything/*",
"plugins": {
"openid-connect": {
"bearer_only": false,
"session": {
"secret": "f86cf31663a9c9fa0a28c2cc78badef1"
},
"use_jwks": true,
"client_id": "'"$OIDC_CLIENT_ID"'",
"client_secret": "'"$OIDC_CLIENT_SECRET"'",
"discovery": "'"$OIDC_DISCOVERY"'",
"scope": "openid profile",
"redirect_uri": "http://localhost:9080/anything/callback"
}
},
"upstream":{
"type":"roundrobin",
"nodes":{
"httpbin.org:80":1
}
}
}'
❶ 使用身份提供商的 JWKS 端点验证令牌。端点从发现文档中获取。
services:
- name: httpbin Service
routes:
- uris:
- /anything/*
name: auth-with-oidc
plugins:
openid-connect:
bearer_only: false
session:
secret: f86cf31663a9c9fa0a28c2cc78badef1
use_jwks: true
client_id: "apisix-quickstart-client"
client_secret: "bSaIN3MV1YynmtXvU8lKkfeY0iwpr9cH"
discovery: "http://192.168.42.145:8080/realms/quickstart-realm/.well-known/openid-configuration"
scope: openid profile
redirect_uri: "http://localhost:9080/anything/callback"
upstream:
type: roundrobin
nodes:
- host: httpbin.org
port: 80
weight: 1
❶ 使用身份提供商的 JWKS 端点验证令牌。端点从发现文档中获取。
将配置同步到 APISIX:
adc sync -f adc.yaml
- Gateway API
- APISIX CRD
apiVersion: apisix.apache.org/v1alpha1
kind: PluginConfig
metadata:
namespace: ingress-apisix
name: auth-plugin-config
spec:
plugins:
- name: openid-connect
config:
bearer_only: false
session:
secret: f86cf31663a9c9fa0a28c2cc78badef1
use_jwks: true
client_id: apisix-quickstart-client
client_secret: bSaIN3MV1YynmtXvU8lKkfeY0iwpr9cH
discovery: http://192.168.117.2:32080/realms/quickstart-realm/.well-known/openid-configuration
scope: openid profile
redirect_uri: http://localhost:9080/anything/callback
---
apiVersion: v1
kind: Service
metadata:
namespace: ingress-apisix
name: httpbin-external-domain
spec:
type: ExternalName
externalName: httpbin.org
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
namespace: ingress-apisix
name: auth-with-oidc
spec:
parentRefs:
- name: apisix
rules:
- matches:
- path:
type: PathPrefix
value: /anything/*
filters:
- type: ExtensionRef
extensionRef:
group: apisix.apache.org
kind: PluginConfig
name: auth-plugin-config
backendRefs:
- name: httpbin-external-domain
port: 80
apiVersion: apisix.apache.org/v2
kind: ApisixUpstream
metadata:
namespace: ingress-apisix
name: httpbin-external-domain
spec:
externalNodes:
- type: Domain
name: httpbin.org
---
apiVersion: apisix.apache.org/v2
kind: ApisixRoute
metadata:
namespace: ingress-apisix
name: auth-with-oidc
spec:
ingressClassName: apisix
http:
- name: auth-with-oidc
match:
paths:
- /anything/*
plugins:
- name: openid-connect
enable: true
config:
bearer_only: false
session:
secret: f86cf31663a9c9fa0a28c2cc78badef1
use_jwks: true
client_id: apisix-quickstart-client
client_secret: bSaIN3MV1YynmtXvU8lKkfeY0iwpr9cH
discovery: http://192.168.117.2:32080/realms/quickstart-realm/.well-known/openid-configuration
scope: openid profile
redirect_uri: http://localhost:9080/anything/callback
upstreams:
- name: httpbin-external-domain
❶ 使用身份提供商的 JWKS 端点验证令牌。端点从发现文档中获取。
将配置应用到你的集群:
kubectl apply -f oidc-route.yaml
自省端点将从发现文档中获取。
使用有效访问令牌验证
在 令牌端点 获取 Keycloak 服务器的访问令牌:
curl -i "http://127.0.0.1:8080/realms/quickstart-realm/protocol/openid-connect/token" -X POST \
-d 'grant_type=client_credentials' \
-d 'client_id='$OIDC_CLIENT_ID'' \
-d 'client_secret='$OIDC_CLIENT_SECRET''
预期的响应类似于以下内容:
{"access_token":"eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJoT3ludlBPY2d6Y3VWWnYtTU42bXZKMUczb0dOX2d6MFo3WFl6S2FSa1NBIn0.eyJleHAiOjE3MDM4MjU1NjQsImlhdCI6MTcwMzgyNTI2NCwianRpIjoiMWQ4NWE4N2UtZDFhMC00NThmLThiMTItNGZiYWM2ODA5YmYwIiwiaXNzIjoiaHR0cDovLzE5Mi4xNjguMS44Mzo4MDgwL3JlYWxtcy9xdWlja3N0YXJ0LXJlYWxtIiwiYXVkIjoiYWNjb3VudCIsInN1YiI6IjE1OGUzOWFlLTk0YjAtNDI3Zi04ZGU3LTU3MTRhYWYwOGYzOSIsInR5cCI6IkJlYXJlciIsImF6cCI6ImFwaXNpeC1xdWlja3N0YXJ0LWNsaWVudCIsImFjciI6IjEiLCJyZWFsbV9hY2Nlc3MiOnsicm9sZXMiOlsiZGVmYXVsdC1yb2xlcy1xdWlja3N0YXJ0LXJlYWxtIiwib2ZmbGluZV9hY2Nlc3MiLCJ1bWFfYXV0aG9yaXphdGlvbiJdfSwicmVzb3VyY2VfYWNjZXNzIjp7ImFjY291bnQiOnsicm9sZXMiOlsibWFuYWdlLWFjY291bnQiLCJtYW5hZ2UtYWNjb3VudC1saW5rcyIsInZpZXctcHJvZmlsZSJdfX0sInNjb3BlIjoiZW1haWwgcHJvZmlsZSIsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwiY2xpZW50SG9zdCI6IjE3Mi4xNy4wLjEiLCJjbGllbnRJZCI6ImFwaXNpeC1xdWlja3N0YXJ0LWNsaWVudCIsInByZWZlcnJlZF91c2VybmFtZSI6InNlcnZpY2UtYWNjb3VudC1hcGlzaXgtcXVpY2tzdGFydC1jbGllbnQiLCJjbGllbnRBZGRyZXNzIjoiMTcyLjE3LjAuMSJ9.TltzSXqrJuVID7aGrb35jn-oc07U_-jugSn-3jKz4A44LwtAsME_8b3qkmR4boMOIht_5pF6bnnp70MFAlg6JKu4_yIQDxF_GAHjnZXEO8OCKhtIKwXm2w-hnnJVIhIdGkIVkbPP0HfILuar_m0hpa53VpPBGYR-OS4pyh0KTUs8MB22xAEqyz9zjCm6SX9vXCqgeVkSpRW2E8NaGEbAdY25uY-ZC4dI_pON87Ey5e8GdD6HQLXQlGIOdCDi3N7k0HDoD9TZRv2bMRPfy4zVYm1ZlClIuF79A-ZBwr0c-XYuq7t6EY0gPGEXB-s0SaKlrIU5S9JBeVXRzYvqAih41g","expires_in":300,"refresh_expires_in":0,"token_type":"Bearer","not-before-policy":0,"scope":"email profile"}
将访问令牌保存到环境变量:
# 替换为你的访问令牌
export ACCESS_TOKEN="eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJoT3ludlBPY2d6Y3VWWnYtTU42bXZKMUczb0dOX2d6MFo3WFl6S2FSa1NBIn0.eyJleHAiOjE3MDM4MjU1NjQsImlhdCI6MTcwMzgyNTI2NCwianRpIjoiMWQ4NWE4N2UtZDFhMC00NThmLThiMTItNGZiYWM2ODA5YmYwIiwiaXNzIjoiaHR0cDovLzE5Mi4xNjguMS44Mzo4MDgwL3JlYWxtcy9xdWlja3N0YXJ0LXJlYWxtIiwiYXVkIjoiYWNjb3VudCIsInN1YiI6IjE1OGUzOWFlLTk0YjAtNDI3Zi04ZGU3LTU3MTRhYWYwOGYzOSIsInR5cCI6IkJlYXJlciIsImF6cCI6ImFwaXNpeC1xdWlja3N0YXJ0LWNsaWVudCIsImFjciI6IjEiLCJyZWFsbV9hY2Nlc3MiOnsicm9sZXMiOlsiZGVmYXVsdC1yb2xlcy1xdWlja3N0YXJ0LXJlYWxtIiwib2ZmbGluZV9hY2Nlc3MiLCJ1bWFfYXV0aG9yaXphdGlvbiJdfSwicmVzb3VyY2VfYWNjZXNzIjp7ImFjY291bnQiOnsicm9sZXMiOlsibWFuYWdlLWFjY291bnQiLCJtYW5hZ2UtYWNjb3VudC1saW5rcyIsInZpZXctcHJvZmlsZSJdfX0sInNjb3BlIjoiZW1haWwgcHJvZmlsZSIsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwiY2xpZW50SG9zdCI6IjE3Mi4xNy4wLjEiLCJjbGllbnRJZCI6ImFwaXNpeC1xdWlja3N0YXJ0LWNsaWVudCIsInByZWZlcnJlZF91c2VybmFtZSI6InNlcnZpY2UtYWNjb3VudC1hcGlzaXgtcXVpY2tzdGFydC1jbGllbnQiLCJjbGllbnRBZGRyZXNzIjoiMTcyLjE3LjAuMSJ9.TltzSXqrJuVID7aGrb35jn-oc07U_-jugSn-3jKz4A44LwtAsME_8b3qkmR4boMOIht_5pF6bnnp70MFAlg6JKu4_yIQDxF_GAHjnZXEO8OCKhtIKwXm2w-hnnJVIhIdGkIVkbPP0HfILuar_m0hpa53VpPBGYR-OS4pyh0KTUs8MB22xAEqyz9zjCm6SX9vXCqgeVkSpRW2E8NaGEbAdY25uY-ZC4dI_pON87Ey5e8GdD6HQLXQlGIOdCDi3N7k0HDoD9TZRv2bMRPfy4zVYm1ZlClIuF79A-ZBwr0c-XYuq7t6EY0gPGEXB-s0SaKlrIU5S9JBeVXRzYvqAih41g"
使用有效访问令牌向路由发送请求:
curl -i "http://127.0.0.1:9080/anything/test" -H "Authorization: Bearer $ACCESS_TOKEN"
HTTP/1.1 200 OK 响应验证了对上游资源的请求已获授权。
使用无效访问令牌验证
使用无效访问令牌向路由发送请求:
curl -i "http://127.0.0.1:9080/anything/test" -H "Authorization: Bearer invalid-access-token"
HTTP/1.1 401 Unauthorized 响应验证了 OIDC 插件拒绝了带有无效访问令牌的请求。
验证无访问令牌
向路由发送不带访问令牌的请求:
curl -i "http://127.0.0.1:9080/anything/test"
HTTP/1.1 401 Unauthorized 响应验证了 OIDC 插件拒绝了没有访问令牌的请求。
实施密码授予
密 码授予是一种交换用户凭证以获取访问令牌的遗留方法。
要实施密码授予,请创建一个带有 openid-connect 插件的路由,如下所示:
- Admin API
- ADC
- Ingress Controller
curl -i "http://127.0.0.1:9180/apisix/admin/routes" -X PUT -d '
{
"id": "auth-with-oidc",
"uri":"/anything/*",
"plugins": {
"openid-connect": {
"use_jwks": true,
"client_id": "'"$OIDC_CLIENT_ID"'",
"client_secret": "'"$OIDC_CLIENT_SECRET"'",
"discovery": "'"$OIDC_DISCOVERY"'",
"scope": "openid profile",
"redirect_uri": "http://localhost:9080/anything/callback"
}
},
"upstream":{
"type":"roundrobin",
"nodes":{
"httpbin.org:80":1
}
}
}'
❶ 使用身份提供商的 JWKS 端点验证令牌。端点从发现文档中获取。
services:
- name: httpbin Service
routes:
- uris:
- /anything/*
name: auth-with-oidc
plugins:
openid-connect:
use_jwks: true
client_id: "apisix-quickstart-client"
client_secret: "bSaIN3MV1YynmtXvU8lKkfeY0iwpr9cH"
discovery: "http://192.168.42.145:8080/realms/quickstart-realm/.well-known/openid-configuration"
scope: openid profile
redirect_uri: "http://localhost:9080/anything/callback"
upstream:
type: roundrobin
nodes:
- host: httpbin.org
port: 80
weight: 1
❶ 使用身份提供商的 JWKS 端点验证令牌。端点从发现文档中获取。
将配置同步到 APISIX:
adc sync -f adc.yaml
- Gateway API
- APISIX CRD
apiVersion: apisix.apache.org/v1alpha1
kind: PluginConfig
metadata:
namespace: ingress-apisix
name: auth-plugin-config
spec:
plugins:
- name: openid-connect
config:
use_jwks: true
client_id: apisix-quickstart-client
client_secret: bSaIN3MV1YynmtXvU8lKkfeY0iwpr9cH
discovery: http://192.168.117.2:32080/realms/quickstart-realm/.well-known/openid-configuration
scope: openid profile
redirect_uri: http://localhost:9080/anything/callback
---
apiVersion: v1
kind: Service
metadata:
namespace: ingress-apisix
name: httpbin-external-domain
spec:
type: ExternalName
externalName: httpbin.org
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
namespace: ingress-apisix
name: auth-with-oidc
spec:
parentRefs:
- name: apisix
rules:
- matches:
- path:
type: PathPrefix
value: /anything/*
filters:
- type: ExtensionRef
extensionRef:
group: apisix.apache.org
kind: PluginConfig
name: auth-plugin-config
backendRefs:
- name: httpbin-external-domain
port: 80
apiVersion: apisix.apache.org/v2
kind: ApisixUpstream
metadata:
namespace: ingress-apisix
name: httpbin-external-domain
spec:
externalNodes:
- type: Domain
name: httpbin.org
---
apiVersion: apisix.apache.org/v2
kind: ApisixRoute
metadata:
namespace: ingress-apisix
name: auth-with-oidc
spec:
ingressClassName: apisix
http:
- name: auth-with-oidc
match:
paths:
- /anything/*
plugins:
- name: openid-connect
enable: true
config:
use_jwks: true
client_id: apisix-quickstart-client
client_secret: bSaIN3MV1YynmtXvU8lKkfeY0iwpr9cH
discovery: http://192.168.117.2:32080/realms/quickstart-realm/.well-known/openid-configuration
scope: openid profile
redirect_uri: http://localhost:9080/anything/callback
upstreams:
- name: httpbin-external-domain
❶ 使用身份提供商的 JWKS 端点验证令牌。端点从发现文档中获取。
将配置应用到你的集群:
kubectl apply -f oidc-route.yaml
使用有效访问令牌验证
在 令牌端点 获取 Keycloak 服务器的访问令牌:
OIDC_USER=quickstart-user
OIDC_PASSWORD=quickstart-user-pass
curl -i "http://127.0.0.1:8080/realms/quickstart-realm/protocol/openid-connect/token" -X POST \
-d 'grant_type=password' \
-d 'client_id='$OIDC_CLIENT_ID'' \
-d 'client_secret='$OIDC_CLIENT_SECRET'' \
-d 'username='$OIDC_USER'' \
-d 'password='$OIDC_PASSWORD''
预期的响应类似于以下内容:
{"access_token":"eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJ6U3FFaXN6VlpuYi1sRWMzZkp0UHNpU1ZZcGs4RGN3dXI1Mkx5V05aQTR3In0.eyJleHAiOjE2ODAxNjA5NjgsImlhdCI6MTY4MDE2MDY2OCwianRpIjoiMzQ5MTc4YjQtYmExZC00ZWZjLWFlYTUtZGY2MzJiMDJhNWY5IiwiaXNzIjoiaHR0cDovLzE5Mi4xNjguNDIuMTQ1OjgwODAvcmVhbG1zL3F1aWNrc3RhcnQtcmVhbG0iLCJhdWQiOiJhY2NvdW50Iiwic3ViIjoiMTg4MTVjM2EtNmQwNy00YTY2LWJjZjItYWQ5NjdmMmIwMTFmIiwidHlwIjoiQmVhcmVyIiwiYXpwIjoiYXBpc2l4LXF1aWNrc3RhcnQtY2xpZW50Iiwic2Vzc2lvbl9zdGF0ZSI6ImIxNmIyNjJlLTEwNTYtNDUxNS1hNDU1LWYyNWUwNzdjY2I3NiIsImFjciI6IjEiLCJyZWFsbV9hY2Nlc3MiOnsicm9sZXMiOlsiZGVmYXVsdC1yb2xlcy1xdWlja3N0YXJ0LXJlYWxtIiwib2ZmbGluZV9hY2Nlc3MiLCJ1bWFfYXV0aG9yaXphdGlvbiJdfSwicmVzb3VyY2VfYWNjZXNzIjp7ImFjY291bnQiOnsicm9sZXMiOlsibWFuYWdlLWFjY291bnQiLCJtYW5hZ2UtYWNjb3VudC1saW5rcyIsInZpZXctcHJvZmlsZSJdfX0sInNjb3BlIjoicHJvZmlsZSBlbWFpbCIsInNpZCI6ImIxNmIyNjJlLTEwNTYtNDUxNS1hNDU1LWYyNWUwNzdjY2I3NiIsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwicHJlZmVycmVkX3VzZXJuYW1lIjoicXVpY2tzdGFydC11c2VyIn0.uD_7zfZv5182aLXu9-YBzBDK0nr2mE4FWb_4saTog2JTqFTPZZa99Gm8AIDJx2ZUcZ_ElkATqNUZ4OpWmL2Se5NecMw3slJReewjD6xgpZ3-WvQuTGpoHdW5wN9-Rjy8ungilrnAsnDA3tzctsxm2w6i9KISxvZrzn5Rbk-GN6fxH01VC5eekkPUQJcJgwuJiEiu70SjGnm21xDN4VGkNRC6jrURoclv3j6AeOqDDIV95kA_MTfBswDFMCr2PQlj5U0RTndZqgSoxwFklpjGV09Azp_jnU7L32_Sq-8coZd0nj5mSdbkJLJ8ZDQDV_PP3HjCP7EHdy4P6TyZ7oGvjw","expires_in":300,"refresh_expires_in":1800,"refresh_token":"eyJhbGciOiJIUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICI0YjFiNTQ3Yi0zZmZjLTQ5YzQtYjE2Ni03YjdhNzIxMjk1ODcifQ.eyJleHAiOjE2ODAxNjI0NjgsImlhdCI6MTY4MDE2MDY2OCwianRpIjoiYzRjNjNlMTEtZTdlZS00ZmEzLWJlNGYtNDMyZWQ4ZmY5OTQwIiwiaXNzIjoiaHR0cDovLzE5Mi4xNjguNDIuMTQ1OjgwODAvcmVhbG1zL3F1aWNrc3RhcnQtcmVhbG0iLCJhdWQiOiJodHRwOi8vMTkyLjE2OC40Mi4xNDU6ODA4MC9yZWFsbXMvcXVpY2tzdGFydC1yZWFsbSIsInN1YiI6IjE4ODE1YzNhLTZkMDctNGE2Ni1iY2YyLWFkOTY3ZjJiMDExZiIsInR5cCI6IlJlZnJlc2giLCJhenAiOiJhcGlzaXgtcXVpY2tzdGFydC1jbGllbnQiLCJzZXNzaW9uX3N0YXRlIjoiYjE2YjI2MmUtMTA1Ni00NTE1LWE0NTUtZjI1ZTA3N2NjYjc2Iiwic2NvcGUiOiJwcm9maWxlIGVtYWlsIiwic2lkIjoiYjE2YjI2...
将访问令牌和刷新令牌保存到环境变量。刷新令牌将在 刷新令牌步骤 中使用。
# 替换为你的访问令牌
export ACCESS_TOKEN="eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJ6U3FFaXN6VlpuYi1sRWMzZkp0UHNpU1ZZcGs4RGN3dXI1Mkx5V05aQTR3In0.eyJleHAiOjE2ODAxNjA5NjgsImlhdCI6MTY4MDE2MDY2OCwianRpIjoiMzQ5MTc4YjQtYmExZC00ZWZjLWFlYTUtZGY2MzJiMDJhNWY5IiwiaXNzIjoiaHR0cDovLzE5Mi4xNjguNDIuMTQ1OjgwODAvcmVhbG1zL3F1aWNrc3RhcnQtcmVhbG0iLCJhdWQiOiJhY2NvdW50Iiwic3ViIjoiMTg4MTVjM2EtNmQwNy00YTY2LWJjZjItYWQ5NjdmMmIwMTFmIiwidHlwIjoiQmVhcmVyIiwiYXpwIjoiYXBpc2l4LXF1aWNrc3RhcnQtY2xpZW50Iiwic2Vzc2lvbl9zdGF0ZSI6ImIxNmIyNjJlLTEwNTYtNDUxNS1hNDU1LWYyNWUwNzdjY2I3NiIsImFjciI6IjEiLCJyZWFsbV9hY2Nlc3MiOnsicm9sZXMiOlsiZGVmYXVsdC1yb2xlcy1xdWlja3N0YXJ0LXJlYWxtIiwib2ZmbGluZV9hY2Nlc3MiLCJ1bWFfYXV0aG9yaXphdGlvbiJdfSwicmVzb3VyY2VfYWNjZXNzIjp7ImFjY291bnQiOnsicm9sZXMiOlsibWFuYWdlLWFjY291bnQiLCJtYW5hZ2UtYWNjb3VudC1saW5rcyIsInZpZXctcHJvZmlsZSJdfX0sInNjb3BlIjoicHJvZmlsZSBlbWFpbCIsInNpZCI6ImIxNmIyNjJlLTEwNTYtNDUxNS1hNDU1LWYyNWUwNzdjY2I3NiIsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwicHJlZmVycmVkX3VzZXJuYW1lIjoicXVpY2tzdGFydC11c2VyIn0.uD_7zfZv5182aLXu9-YBzBDK0nr2mE4FWb_4saTog2JTqFTPZZa99Gm8AIDJx2ZUcZ_ElkATqNUZ4OpWmL2Se5NecMw3slJReewjD6xgpZ3-WvQuTGpoHdW5wN9-Rjy8ungilrnAsnDA3tzctsxm2w6i9KISxvZrzn5Rbk-GN6fxH01VC5eekkPUQJcJgwuJiEiu70SjGnm21xDN4VGkNRC6jrURoclv3j6AeOqDDIV95kA_MTfBswDFMCr2PQlj5U0RTndZqgSoxwFklpjGV09Azp_jnU7L32_Sq-8coZd0nj5mSdbkJLJ8ZDQDV_PP3HjCP7EHdy4P6TyZ7oGvjw"
export REFRESH_TOKEN="eyJhbGciOiJIUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICI0YjFiNTQ3Yi0zZmZjLTQ5YzQtYjE2Ni03YjdhNzIxMjk1ODcifQ.eyJleHAiOjE2ODAxNjI0NjgsImlhdCI6MTY4MDE2MDY2OCwianRpIjoiYzRjNjNlMTEtZTdlZS00ZmEzLWJlNGYtNDMyZWQ4ZmY5OTQwIiwiaXNzIjoiaHR0cDovLzE5Mi4xNjguNDIuMTQ1OjgwODAvcmVhbG1zL3F1aWNrc3RhcnQtcmVhbG0iLCJhdWQiOiJodHRwOi8vMTkyLjE2OC40Mi4xNDU6ODA4MC9yZWFsbXMvcXVpY2tzdGFydC1yZWFsbSIsInN1YiI6IjE4ODE1YzNhLTZkMDctNGE2Ni1iY2YyLWFkOTY3ZjJiMDExZiIsInR5cCI6IlJlZnJlc2giLCJhenAiOiJhcGlzaXgtcXVpY2tzdGFydC1jbGllbnQiLCJzZXNzaW9uX3N0YXRlIjoiYjE2YjI2MmUtMTA1Ni00NTE1LWE0NTUtZjI1ZTA3N2NjYjc2Iiwic2NvcGUiOiJwcm9maWxlIGVtYWlsIiwic2lkIjoiYjE2YjI2MmUtMTA1Ni00NTE1LWE0NTUtZjI1ZTA3N2NjYjc2In0.8xYP4bhDg1U9B5cTaEVD7B4oxNp8wwAYEynUne_Jm78"
使用有效访问令牌向路由发送请求:
curl -i "http://127.0.0.1:9080/anything/test" -H "Authorization: Bearer $ACCESS_TOKEN"
HTTP/1.1 200 OK 响应验证了对上游资源的请求已获授权。
使用无效访问令牌验证
使用无效访问令牌向路由发送请求:
curl -i "http://127.0.0.1:9080/anything/test" -H "Authorization: Bearer invalid-access-token"
HTTP/1.1 401 Unauthorized 响应验证了 OIDC 插件拒绝了带有无效访问令牌的请求。
验证无访问令牌
向路由发送不带访问令牌的请求:
curl -i "http://127.0.0.1:9080/anything/test"
HTTP/1.1 401 Unauthorized 响应验证了 OIDC 插件拒绝了没有访问令牌的请求。
刷新令牌
要刷新访问令牌, 请按如下方式向 Keycloak 令牌端点发送请求:
curl -i "http://127.0.0.1:8080/realms/quickstart-realm/protocol/openid-connect/token" -X POST \
-d 'grant_type=refresh_token' \
-d 'client_id='$OIDC_CLIENT_ID'' \
-d 'client_secret='$OIDC_CLIENT_SECRET'' \
-d 'refresh_token='$REFRESH_TOKEN''
你应该看到类似以下的响应,其中包含新的访问令牌和刷新令牌,你可以将其用于后续请求和令牌刷新:
{"access_token":"eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJTdnVwLXlPMHhDdTJBVi1za2pCZ0h6SHZNaG1mcDVDQWc0NHpYb2QxVTlNIn0.eyJleHAiOjE3MzAyNzQ3NDUsImlhdCI6MTczMDI3NDQ0NSwianRpIjoiMjk2Mjk5MWUtM2ExOC00YWFiLWE0NzAtODgxNWEzNjZjZmM4IiwiaXNzIjoiaHR0cDovLzE5Mi4xNjguMTUyLjU6ODA4MC9yZWFsbXMvcXVpY2tzdGFydC1yZWFsbSIsImF1ZCI6ImFjY291bnQiLCJzdWIiOiI2ZWI0ZTg0Yy00NmJmLTRkYzUtOTNkMC01YWM5YzE5MWU0OTciLCJ0eXAiOiJCZWFyZXIiLCJhenAiOiJhcGlzaXgtcXVpY2tzdGFydC1jbGllbnQiLCJzZXNzaW9uX3N0YXRlIjoiNTU2ZTQyYjktMjE2Yi00NTEyLWE5ZjAtNzE3ZTAyYTQ4MjZhIiwiYWNyIjoiMSIsInJlYWxtX2FjY2VzcyI6eyJyb2xlcyI6WyJkZWZhdWx0LXJvbGVzLXF1aWNrc3RhcnQtcmVhbG0iLCJvZmZsaW5lX2FjY2VzcyIsInVtYV9hdXRob3JpemF0aW9uIl19LCJyZXNvdXJjZV9hY2Nlc3MiOnsiYWNjb3VudCI6eyJyb2xlcyI6WyJtYW5hZ2UtYWNjb3VudCIsIm1hbmFnZS1hY2NvdW50LWxpbmtzIiwidmlldy1wcm9maWxlIl19fSwic2NvcGUiOiJlbWFpbCBwcm9maWxlIiwic2lkIjoiNTU2ZTQyYjktMjE2Yi00NTEyLWE5ZjAtNzE3ZTAyYTQ4MjZhIiwiZW1haWxfdmVyaWZpZWQiOmZhbHNlLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJxdWlja3N0YXJ0LXVzZXIifQ.KLqn1LQdazoPBqLLR856C35XpqbMO9I7WFt3KrDxZF1N8vwv4AvZYWI_2rsbdjCakh9JmPgyYRgEGufYLiDBsqy9CrMVejAIJPYsJIonIXBCp5Ysu92ODJuqtTKuuJ6K7dam7fisBFfCBbVvGspnZ3p0caedpOaF_kSd-F8ARHKVsmkuX3_ucDrP3UctjEXHezefTY4YHjNMB9wuMDPXX2vXt2BsOasnznsIHHHX-ZH8JY6eEfWPtfx0qAED6lVZICT6Rqj_j5-Cf9ogzFtLyy_XvtG9BbHME2B8AXYpxdzqxOxmVVbZdrB8elfmFjs1R3vUn2r3xA9hO_znZo_IoQ","expires_in":300,"refresh_expires_in":1800,"refresh_token":"eyJhbGciOiJIUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICIwYWYwZTAwYy0xMThjLTRkNDktYmIwMS1iMDIwNDE3MmFjMzIifQ.eyJleHAiOjE3MzAyNzYyNDUsImlhdCI6MTczMDI3NDQ0NSwianRpIjoiZGQyZTJmYTktN2Y3Zi00MjM5LWEwODAtNWQyZDFiZTdjNzk4IiwiaXNzIjoiaHR0cDovLzE5Mi4xNjguMTUyLjU6ODA4MC9yZWFsbXMvcXVpY2tzdGFydC1yZWFsbSIsImF1ZCI6Imh0dHA6Ly8xOTIuMTY4LjE1Mi41OjgwODAvcmVhbG1zL3F1aWNrc3RhcnQtcmVhbG0iLCJzdWIiOiI2ZWI0ZTg0Yy00NmJmLTRkYzUtOTNkMC01YWM5YzE5MWU0OTciLCJ0eXAiOiJSZWZyZXNoIiwiYXpwIjoiYXBpc2l4LXF1aWNrc3RhcnQtY2xpZW50Iiwic2Vzc2lvbl9zdGF0ZSI6IjU1NmU0MmI5LTIxNmItNDUxMi1hOWYwLTcxN2UwMmE0ODI2YSIsInNjb3BlIjoiZW1haWwgcHJvZmlsZSIsInNpZCI6IjU1NmU0MmI5L...
下一步
APISIX 支持与许多其他 OIDC 身份提供商集成,例如 Okta、Auth0、Authgear 和 Microsoft Entra ID (Azure AD)。