authz-keycloak
authz-keycloak 插件支持与 Keycloak 集成以对用户进行身份认证和授权。有关此插件中可用配置选项的更多信息,请参阅 Keycloak 的 授权服务指南。
虽然该插件是为 Keycloak 开发的,但理论上它可以与其他符合 OAuth/OIDC 和 UMA 标准的身份提供商一起使用。
示例
以下示例演示了如何在不同场景下配置 authz-keycloak。
在开始之前,请完成 Keycloak 的 初步设置。
设置 Keycloak
启动 Keycloak
- Docker
- Kubernetes
在 Docker 中以 开发模式 启动一个名为 apisix-quickstart-keycloak 的 Keycloak 实例,管理员用户名为 quickstart-admin,密码为 quickstart-admin-pass:
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 IP 保存到环境变量中,以便在后续配置中引用:
apiVersion: apps/v1
kind: Deployment
metadata:
namespace: aic
name: keycloak
spec:
replicas: 1
selector:
matchLabels:
app: keycloak
template:
metadata:
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
---
apiVersion: v1
kind: Service
metadata:
namespace: aic
name: keycloak
spec:
selector:
app: keycloak
ports:
- port: 8080
targetPort: 8080
应用清单:
kubectl apply -f keycloak.yaml
如需在本地打开 Keycloak 控制台,请对 Service 执行端口转发:
kubectl port-forward -n aic svc/keycloak 8080:8080
保存 Keycloak URL
将 Keycloak URL 保存到环境变量中,供后续配置引用:
- Docker
- Kubernetes
KEYCLOAK_URL=http://192.168.42.145:8080 # replace with your Keycloak URL
KEYCLOAK_URL=http://keycloak.aic.svc.cluster.local:8080
创建 Realm、客户端和授权对象
在浏览器中访问 http://localhost:8080 并点击 Administration Console:

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

创建名为 quickstart-realm 的 Realm:

创建名为 apisix-quickstart-client 的客户端:

在客户端设置页面,将访问类型选择为 confidential:

为客户端启用授权并保存配置。此操作还应启用客户端服务账户,并自动分配 uma_protection 角色:

创建名为 httpbin-access 的客户端作用域:

在客户端的 Authorization 部分创建授权作用域 access:

创建 URI 为 /anything、作用域为 access 的资源 httpbin-anything:

创建要求 httpbin-access 的客户端作用域策略 access-client-scope-policy:

创建基于作用域的权限 access-scope-perm,该权限使用 access 作用域和 access-client-scope-policy:

将 httpbin-access 添加到 apisix-quickstart-client 的默认客户端作用域:

创建名为 quickstart-user 的用户:

将密码设置为 quickstart-user-pass,并关闭 Temporary:

点击 Clients > apisix-quickstart-client > Credentials,并从 Secret 中复制客户端密钥:

将 OIDC Client ID 和 Secret 保存到环境变量:
OIDC_CLIENT_ID=apisix-quickstart-client
OIDC_CLIENT_SECRET=replace-with-your-client-secret
如果 APISIX 在 Kubernetes 中运行,请确保插件配置和 Token 请求始终使用相同的 Keycloak 主机名。否则,Token 签发者与配置的授权端点不匹配时,Keycloak 可能会拒绝 Bearer Token。
请求访问令牌 (Access Token)
从 Keycloak 请求访问令牌,并将其保存到 ACCESS_TOKEN:
- Docker
- Kubernetes
ACCESS_TOKEN=$(curl -sS "$KEYCLOAK_URL/realms/quickstart-realm/protocol/openid-connect/token" \
-d 'grant_type=client_credentials' \
-d 'client_id='$OIDC_CLIENT_ID'' \
-d 'client_secret='$OIDC_CLIENT_SECRET'' | jq -r '.access_token')
在 Keycloak Pod 内执行令牌请求,并将结果保存到 ACCESS_TOKEN:
ACCESS_TOKEN=$(kubectl exec -n aic deploy/keycloak -- env OIDC_CLIENT_SECRET="$OIDC_CLIENT_SECRET" sh -lc 'curl -sS "http://keycloak.aic.svc.cluster.local:8080/realms/quickstart-realm/protocol/openid-connect/token" \
-d grant_type=client_credentials \
-d client_id=apisix-quickstart-client \
-d client_secret="$OIDC_CLIENT_SECRET"' | jq -r '.access_token')
使用延迟加载路径和资源注册端点
以下示例演示了如何配置 authz-keycloak 插件,使其使用资源注册端点将 请求 URI 动态解析为一个或多个资源,而不是使用静态权限。
- Admin API
- ADC
- Ingress Controller
创建一个路由 authz-keycloak-route 如下:
curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"id": "authz-keycloak-route",
"uri": "/anything",
"plugins": {
"authz-keycloak": {
"lazy_load_paths": true,
"resource_registration_endpoint": "'"$KEYCLOAK_URL"'/realms/quickstart-realm/authz/protection/resource_set",
"discovery": "'"$KEYCLOAK_URL"'/realms/quickstart-realm/.well-known/uma2-configuration",
"client_id": "'"$OIDC_CLIENT_ID"'",
"client_secret": "'"$OIDC_CLIENT_SECRET"'"
}
},
"upstream": {
"type": "roundrobin",
"nodes": {
"httpbin.org:80": 1
}
}
}'
在 ADC 中创建配置了 authz-keycloak 插件的路由:
services:
- name: authz-keycloak-service
routes:
- name: authz-keycloak-route
uris:
- /anything
plugins:
authz-keycloak:
lazy_load_paths: true
resource_registration_endpoint: ${KEYCLOAK_URL}/realms/quickstart-realm/authz/protection/resource_set
discovery: ${KEYCLOAK_URL}/realms/quickstart-realm/.well-known/uma2-configuration
client_id: ${OIDC_CLIENT_ID}
client_secret: ${OIDC_CLIENT_SECRET}
upstream:
type: roundrobin
nodes:
- host: httpbin.org
port: 80
weight: 1
将配置同步到网关:
adc sync -f adc.yaml
在路由上配置 authz-keycloak 插件:
- Gateway API
- APISIX CRD
apiVersion: v1
kind: Service
metadata:
namespace: aic
name: httpbin-external-domain
spec:
type: ExternalName
externalName: httpbin.org
---
apiVersion: apisix.apache.org/v1alpha1
kind: PluginConfig
metadata:
namespace: aic
name: authz-keycloak-plugin-config
spec:
plugins:
- name: authz-keycloak
config:
lazy_load_paths: true
resource_registration_endpoint: http://keycloak.aic.svc.cluster.local:8080/realms/quickstart-realm/authz/protection/resource_set
discovery: http://keycloak.aic.svc.cluster.local:8080/realms/quickstart-realm/.well-known/uma2-configuration
client_id: apisix-quickstart-client
client_secret: replace-with-your-client-secret
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
namespace: aic
name: authz-keycloak-route
spec:
parentRefs:
- name: apisix
rules:
- matches:
- path:
type: Exact
value: /anything
filters:
- type: ExtensionRef
extensionRef:
group: apisix.apache.org
kind: PluginConfig
name: authz-keycloak-plugin-config
backendRefs:
- name: httpbin-external-domain
port: 80
将配置应用到集群:
kubectl apply -f authz-keycloak-ic.yaml
apiVersion: apisix.apache.org/v2
kind: ApisixUpstream
metadata:
namespace: aic
name: httpbin-external-domain
spec:
ingressClassName: apisix
externalNodes:
- type: Domain
name: httpbin.org
---
apiVersion: apisix.apache.org/v2
kind: ApisixPluginConfig
metadata:
namespace: aic
name: authz-keycloak-plugin-config
spec:
ingressClassName: apisix
plugins:
- name: authz-keycloak
enable: true
config:
lazy_load_paths: true
resource_registration_endpoint: http://keycloak.aic.svc.cluster.local:8080/realms/quickstart-realm/authz/protection/resource_set
discovery: http://keycloak.aic.svc.cluster.local:8080/realms/quickstart-realm/.well-known/uma2-configuration
client_id: apisix-quickstart-client
client_secret: replace-with-your-client-secret
---
apiVersion: apisix.apache.org/v2
kind: ApisixRoute
metadata:
namespace: aic
name: authz-keycloak-route
spec:
ingressClassName: apisix
http:
- name: authz-keycloak-route
match:
paths:
- /anything
methods:
- GET
upstreams:
- name: httpbin-external-domain
plugin_config_name: authz-keycloak-plugin-config
将配置应用到集群:
kubectl apply -f authz-keycloak-ic.yaml
❶ 将 lazy_load_paths 设置为 true。
❷ 将 resource_registration_endpoint 设置为 Keycloak 的 UMA 兼容资源注册端点。当 lazy_load_paths 为 true 时必填。
❸ 将 discovery 设置为 Keycloak 授权服务的发现文档端点。
❹ 将 client_id 设置为之前创建的 Client ID。
❺ 将 client_secret 设置为之前创建的 Client Secret。当 lazy_load_paths 为 true 时必填。
向路由发送请求:
curl "http://127.0.0.1:9080/anything" -H "Authorization: Bearer $ACCESS_TOKEN"
你应该会看到类似于以下的 HTTP/1.1 200 OK 响应:
{
"args": {},
"data": "",
"files": {},
"form": {},
"headers": {
"Accept": "*/*",
"Authorization": "Bearer eyJhbGciOiJSU..."
},
"json": null,
"method": "GET",
"url": "http://127.0.0.1/anything"
}
使用静态权限
以下示例演示了如何配置 authz-keycloak 插件,使其使用静态权限 httpbin-anything#access。
- Admin API
- ADC
- Ingress Controller
创建一个路由 authz-keycloak-route 如下:
curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"id": "authz-keycloak-route",
"uri": "/anything",
"plugins": {
"authz-keycloak": {
"lazy_load_paths": false,
"discovery": "'"$KEYCLOAK_URL"'/realms/quickstart-realm/.well-known/uma2-configuration",
"permissions": ["httpbin-anything#access"],
"client_id": "'"$OIDC_CLIENT_ID"'"
}
},
"upstream": {
"type": "roundrobin",
"nodes": {
"httpbin.org:80": 1
}
}
}'
在 ADC 中创建配置了 authz-keycloak 插件的路由:
services:
- name: authz-keycloak-service
routes:
- name: authz-keycloak-route
uris:
- /anything
plugins:
authz-keycloak:
lazy_load_paths: false
discovery: ${KEYCLOAK_URL}/realms/quickstart-realm/.well-known/uma2-configuration
permissions:
- "httpbin-anything#access"
client_id: ${OIDC_CLIENT_ID}
upstream:
type: roundrobin
nodes:
- host: httpbin.org
port: 80
weight: 1
将配置同步到网关:
adc sync -f adc.yaml
在路由上配置 authz-keycloak 插件:
- Gateway API
- APISIX CRD
apiVersion: v1
kind: Service
metadata:
namespace: aic
name: httpbin-external-domain
spec:
type: ExternalName
externalName: httpbin.org
---
apiVersion: apisix.apache.org/v1alpha1
kind: PluginConfig
metadata:
namespace: aic
name: authz-keycloak-plugin-config
spec:
plugins:
- name: authz-keycloak
config:
lazy_load_paths: false
discovery: http://keycloak.aic.svc.cluster.local:8080/realms/quickstart-realm/.well-known/uma2-configuration
permissions:
- "httpbin-anything#access"
client_id: apisix-quickstart-client
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
namespace: aic
name: authz-keycloak-route
spec:
parentRefs:
- name: apisix
rules:
- matches:
- path:
type: Exact
value: /anything
filters:
- type: ExtensionRef
extensionRef:
group: apisix.apache.org
kind: PluginConfig
name: authz-keycloak-plugin-config
backendRefs:
- name: httpbin-external-domain
port: 80
将配置应用到集群:
kubectl apply -f authz-keycloak-ic.yaml
apiVersion: apisix.apache.org/v2
kind: ApisixUpstream
metadata:
namespace: aic
name: httpbin-external-domain
spec:
ingressClassName: apisix
externalNodes:
- type: Domain
name: httpbin.org
---
apiVersion: apisix.apache.org/v2
kind: ApisixPluginConfig
metadata:
namespace: aic
name: authz-keycloak-plugin-config
spec:
ingressClassName: apisix
plugins:
- name: authz-keycloak
enable: true
config:
lazy_load_paths: false
discovery: http://keycloak.aic.svc.cluster.local:8080/realms/quickstart-realm/.well-known/uma2-configuration
permissions:
- "httpbin-anything#access"
client_id: apisix-quickstart-client
---
apiVersion: apisix.apache.org/v2
kind: ApisixRoute
metadata:
namespace: aic
name: authz-keycloak-route
spec:
ingressClassName: apisix
http:
- name: authz-keycloak-route
match:
paths:
- /anything
methods:
- GET
upstreams:
- name: httpbin-external-domain
plugin_config_name: authz-keycloak-plugin-config
将配置应用到集群:
kubectl apply -f authz-keycloak-ic.yaml
❶ 将 lazy_load_paths 设置为 false。
❷ 将 discovery 设置为 Keycloak 授权服务的发现文档端点。
❸ 将 permissions 设置为资源 httpbin-anything 和 Scope access。
向路由发送请求:
curl "http://127.0.0.1:9080/anything" -H "Authorization: Bearer $ACCESS_TOKEN"
你应该会看到类似于以下的 HTTP/1.1 200 OK 响应:
如果你移除了 apisix-quickstart-client 的 Client Scope httpbin-access,请求该资源时你应该会收到 401 Unauthorized 响应。
在自定义令牌端点使用密码模式生成令牌
以下示例演示了如何配置 authz-keycloak 插件,使其在自定义端点使用密码模式请求令牌。
- Admin API
- ADC
- Ingress Controller
创建一个路由 authz-keycloak-route 如下:
curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"id": "authz-keycloak-route",
"uri": "/api/*",
"plugins": {
"authz-keycloak": {
"lazy_load_paths": true,
"resource_registration_endpoint": "'"$KEYCLOAK_URL"'/realms/quickstart-realm/authz/protection/resource_set",
"client_id": "'"$OIDC_CLIENT_ID"'",
"client_secret": "'"$OIDC_CLIENT_SECRET"'",
"token_endpoint": "'"$KEYCLOAK_URL"'/realms/quickstart-realm/protocol/openid-connect/token",
"password_grant_token_generation_incoming_uri": "/api/token"
}
},
"upstream": {
"type": "roundrobin",
"nodes": {
"httpbin.org:80": 1
}
}
}'
在 ADC 中创建配置了 authz-keycloak 插件的路由:
services:
- name: authz-keycloak-service
routes:
- name: authz-keycloak-route
uris:
- /api/*
plugins:
authz-keycloak:
lazy_load_paths: true
resource_registration_endpoint: ${KEYCLOAK_URL}/realms/quickstart-realm/authz/protection/resource_set
client_id: ${OIDC_CLIENT_ID}
client_secret: ${OIDC_CLIENT_SECRET}
token_endpoint: ${KEYCLOAK_URL}/realms/quickstart-realm/protocol/openid-connect/token
password_grant_token_generation_incoming_uri: /api/token
upstream:
type: roundrobin
nodes:
- host: httpbin.org
port: 80
weight: 1
将配置同步到网关:
adc sync -f adc.yaml
在路由上配置 authz-keycloak 插件:
- Gateway API
- APISIX CRD
apiVersion: v1
kind: Service
metadata:
namespace: aic
name: httpbin-external-domain
spec:
type: ExternalName
externalName: httpbin.org
---
apiVersion: apisix.apache.org/v1alpha1
kind: PluginConfig
metadata:
namespace: aic
name: authz-keycloak-plugin-config
spec:
plugins:
- name: authz-keycloak
config:
lazy_load_paths: true
resource_registration_endpoint: http://keycloak.aic.svc.cluster.local:8080/realms/quickstart-realm/authz/protection/resource_set
client_id: apisix-quickstart-client
client_secret: replace-with-your-client-secret
token_endpoint: http://keycloak.aic.svc.cluster.local:8080/realms/quickstart-realm/protocol/openid-connect/token
password_grant_token_generation_incoming_uri: /api/token
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
namespace: aic
name: authz-keycloak-route
spec:
parentRefs:
- name: apisix
rules:
- matches:
- path:
type: PathPrefix
value: /api/
filters:
- type: ExtensionRef
extensionRef:
group: apisix.apache.org
kind: PluginConfig
name: authz-keycloak-plugin-config
backendRefs:
- name: httpbin-external-domain
port: 80
将配置应用到集群:
kubectl apply -f authz-keycloak-ic.yaml
apiVersion: apisix.apache.org/v2
kind: ApisixUpstream
metadata:
namespace: aic
name: httpbin-external-domain
spec:
ingressClassName: apisix
externalNodes:
- type: Domain
name: httpbin.org
---
apiVersion: apisix.apache.org/v2
kind: ApisixPluginConfig
metadata:
namespace: aic
name: authz-keycloak-plugin-config
spec:
ingressClassName: apisix
plugins:
- name: authz-keycloak
enable: true
config:
lazy_load_paths: true
resource_registration_endpoint: http://keycloak.aic.svc.cluster.local:8080/realms/quickstart-realm/authz/protection/resource_set
client_id: apisix-quickstart-client
client_secret: replace-with-your-client-secret
token_endpoint: http://keycloak.aic.svc.cluster.local:8080/realms/quickstart-realm/protocol/openid-connect/token
password_grant_token_generation_incoming_uri: /api/token
---
apiVersion: apisix.apache.org/v2
kind: ApisixRoute
metadata:
namespace: aic
name: authz-keycloak-route
spec:
ingressClassName: apisix
http:
- name: authz-keycloak-route
match:
paths:
- /api/*
methods:
- GET
- POST
upstreams:
- name: httpbin-external-domain
plugin_config_name: authz-keycloak-plugin-config
将配置应用到集群:
kubectl apply -f authz-keycloak-ic.yaml
❶ 将 token_endpoint 设置为 Keycloak 令牌端点。当未提供发现文档时必填。
❷ 将 password_grant_token_generation_incoming_uri 设置为用户可以获取令牌的自定义 URI 路径。
向配置的令牌端点发送请求。注意请求应使用 POST 方法,并且 Content-Type 为 application/x-www-form-urlencoded:
OIDC_USER=quickstart-user
OIDC_PASSWORD=quickstart-user-pass
curl "http://127.0.0.1:9080/api/token" -X POST \
-H "Content-Type: application/x-www-form-urlencoded" \
-H "Accept: application/json" \
-d 'username='$OIDC_USER'' \
-d 'password='$OIDC_PASSWORD''
你应该会看到包含访问令牌的 JSON 响应,类似于以下内容:
{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIi...",
"expires_in": 300,
"refresh_expires_in": 1800,
"token_type": "Bearer",
"scope": "profile email httpbin-access"
}