跳到主要内容

authz-keycloak

authz-keycloak 插件支持与 Keycloak 集成以对用户进行身份认证和授权。有关此插件中可用配置选项的更多信息,请参阅 Keycloak 的 授权服务指南

虽然该插件是为 Keycloak 开发的,但理论上它可以与其他符合 OAuth/OIDC 和 UMA 标准的身份提供商一起使用。

示例

以下示例演示了如何在不同场景下配置 authz-keycloak

在开始之前,请完成 Keycloak 的 初步设置

设置 Keycloak

启动 Keycloak

在 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 URL

将 Keycloak URL 保存到环境变量中,供后续配置引用:

KEYCLOAK_URL=http://192.168.42.145:8080 # replace with your Keycloak URL

创建 Realm、客户端和授权对象

在浏览器中访问 http://localhost:8080 并点击 Administration Console

突出显示 Administration Console 入口的 Keycloak 欢迎页面

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

Keycloak 管理员登录表单

创建名为 quickstart-realm 的 Realm:

Realm 名称设为 quickstart-realm 且突出显示 Create 按钮的 Keycloak Add realm 表单

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

Keycloak Add client 表单

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

访问类型设为 confidential 的 Keycloak 客户端设置

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

Service Accounts Enabled 和 Authorization Enabled 开关均设为 ON 的 Keycloak 客户端 Settings 标签页

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

保存新的 Keycloak 客户端作用域

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

突出显示 Create 按钮的 Keycloak 客户端 Authorization > Authorization Scopes 标签页

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

列出 Default Resource 且突出显示 Create 按钮的 Keycloak 客户端 Authorization > Resources 标签页

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

列出 Default Policy 且突出显示 Create Policy 下拉菜单的 Keycloak 客户端 Authorization > Policies 标签页

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

在 Keycloak 中添加基于作用域的权限

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

将客户端作用域关联到 Keycloak 客户端

创建名为 quickstart-user 的用户:

保存新的 Keycloak 用户

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

为 Keycloak 用户设置密码

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

显示已生成客户端 Secret 的 Keycloak 客户端 Credentials 标签页

将 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

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')

使用延迟加载路径和资源注册端点

以下示例演示了如何配置 authz-keycloak 插件,使其使用资源注册端点将请求 URI 动态解析为一个或多个资源,而不是使用静态权限。

创建一个路由 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
}
}
}'

❶ 将 lazy_load_paths 设置为 true

❷ 将 resource_registration_endpoint 设置为 Keycloak 的 UMA 兼容资源注册端点。当 lazy_load_pathstrue 时必填。

❸ 将 discovery 设置为 Keycloak 授权服务的发现文档端点。

❹ 将 client_id 设置为之前创建的 Client ID。

❺ 将 client_secret 设置为之前创建的 Client Secret。当 lazy_load_pathstrue 时必填。

向路由发送请求:

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

创建一个路由 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
}
}
}'

❶ 将 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 插件,使其在自定义端点使用密码模式请求令牌。

创建一个路由 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
}
}
}'

❶ 将 token_endpoint 设置为 Keycloak 令牌端点。当未提供发现文档时必填。

❷ 将 password_grant_token_generation_incoming_uri 设置为用户可以获取令牌的自定义 URI 路径。

向配置的令牌端点发送请求。注意请求应使用 POST 方法,并且 Content-Typeapplication/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"
}