forward-auth
forward-auth 插件支持与外部授权服务集成以进行认证和授权。如果认证失败,将向客户端返回可自定义的错误消息。如果认证成功,请求将连同 APISIX 添加的以下请求头一起转发到上游服务:
X-Forwarded-Proto: 协议X-Forwarded-Method: HTTP 方法X-Forwarded-Host: 主机X-Forwarded-Uri: URIX-Forwarded-For: 源 IP
示例
以下示例演示了如何在不同场景下使用 forward-auth。
要跟随前两个示例,请先设置好你的外部授权服务,或者使用 serverless function 插件 创建一个模拟认证服务,如下所示:
- Admin API
- ADC
- Ingress Controller
curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-H 'Content-Type: application/json' \
-d '{
"id": "auth-mock",
"uri": "/auth",
"plugins": {
"serverless-pre-function": {
"phase": "rewrite",
"functions": [
"return function (conf, ctx)
local core = require(\"apisix.core\");
local authorization = core.request.header(ctx, \"Authorization\");
if authorization == \"123\" then
core.response.exit(200);
elseif authorization == \"321\" then
core.response.set_header(\"X-User-ID\", \"i-am-user\");
core.response.exit(200);
else core.response.set_header(\"X-Forward-Auth\", \"Fail\");
core.response.exit(403);
end
end"
]
}
}
}'
❶ 如果 Authorization 头的值为 123,响应 200 OK;
❷ 如果 Authorization 头的值为 321,设置头 X-User-ID: i-am-user 并响应 200 OK;
❸ 否则,设置头 X-Forward-Auth: Fail 并响应 403 Forbidden。
services:
- name: auth-mock-service
routes:
- name: auth-mock-route
uris:
- /auth
plugins:
serverless-pre-function:
phase: rewrite
functions:
- |
return function(conf, ctx)
local core = require("apisix.core")
local authorization = core.request.header(ctx, "Authorization")
if authorization == "123" then
core.response.exit(200)
elseif authorization == "321" then
core.response.set_header("X-User-ID", "i-am-user")
core.response.exit(200)
else
core.response.set_header("X-Forward-Auth", "Fail")
core.response.exit(403)
end
end
upstream:
type: roundrobin
nodes:
- host: httpbin.org
port: 80
weight: 1
❶ 如果 Authorization 头的值为 123,响应 200 OK;
❷ 如果 Authorization 头的值为 321,设 置头 X-User-ID: i-am-user 并响应 200 OK;
❸ 否则,设置头 X-Forward-Auth: Fail 并响应 403 Forbidden。
将配置同步到网关:
adc sync -f adc-auth-mock.yaml
- 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: auth-mock-plugin-config
spec:
plugins:
- name: serverless-pre-function
config:
phase: rewrite
functions:
- |
return function(conf, ctx)
local core = require("apisix.core")
local authorization = core.request.header(ctx, "Authorization")
if authorization == "123" then
core.response.exit(200)
elseif authorization == "321" then
core.response.set_header("X-User-ID", "i-am-user")
core.response.exit(200)
else
core.response.set_header("X-Forward-Auth", "Fail")
core.response.exit(403)
end
end
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
namespace: aic
name: auth-mock-route
spec:
parentRefs:
- name: apisix
rules:
- matches:
- path:
type: Exact
value: /auth
filters:
- type: ExtensionRef
extensionRef:
group: apisix.apache.org
kind: PluginConfig
name: auth-mock-plugin-config
backendRefs:
- name: httpbin-external-domain
port: 80
❶ 如果 Authorization 头的值为 123,响应 200 OK;
❷ 如果 Authorization 头的值为 321,设置头 X-User-ID: i-am-user 并响应 200 OK;
❸ 否则,设置头 X-Forward-Auth: Fail 并响应 403 Forbidden。
上述函数实现了以下逻辑:
kubectl apply -f forward-auth-mock-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: ApisixRoute
metadata:
namespace: aic
name: auth-mock-route
spec:
ingressClassName: apisix
http:
- name: auth-mock-route
match:
paths:
- /auth
upstreams:
- name: httpbin-external-domain
plugins:
- name: serverless-pre-function
enable: true
config:
phase: rewrite
functions:
- |
return function(conf, ctx)
local core = require("apisix.core")
local authorization = core.request.header(ctx, "Authorization")
if authorization == "123" then
core.response.exit(200)
elseif authorization == "321" then
core.response.set_header("X-User-ID", "i-am-user")
core.response.exit(200)
else
core.response.set_header("X-Forward-Auth", "Fail")
core.response.exit(403)
end
end
❶ 如果 Authorization 头的值为 123,响应 200 OK;
❷ 如果 Authorization 头的值为 321,设置头 X-User-ID: i-am-user 并响应 200 OK;
❸ 否则,设置头 X-Forward-Auth: Fail 并响应 403 Forbidden。
上述函数实现了以下逻辑:
kubectl apply -f forward-auth-mock-ic.yaml
转发指定头到上游资源
以下示例演示了如何在路由上设置 forward-auth,根据请求头中的值控制客户端对上游资源的访问。它还允许将授权服务中的特定头传递到上游资源。
创建一个启用了 forward-auth 插件的路由:
- Admin API
- ADC
- Ingress Controller
curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"id": "forward-auth-route",
"uri": "/headers",
"plugins": {
"forward-auth": {
"uri": "http://127.0.0.1:9080/auth",
"request_headers": ["Authorization"],
"upstream_headers": ["X-User-ID"]
}
},
"upstream": {
"nodes": {
"httpbin.org:80": 1
},
"type": "roundrobin"
}
}'
❶ 授权服务的 URI。
❷ 应转发到授权服务的请求头。
❷ 需要转发到授权服务的请求头。
services:
- name: forward-auth-service
routes:
- name: forward-auth-route
uris:
- /headers
plugins:
forward-auth:
uri: http://127.0.0.1:9080/auth
request_headers:
- Authorization
upstream_headers:
- X-User-ID
upstream:
type: roundrobin
nodes:
- host: httpbin.org
port: 80
weight: 1
❶ 授权服务的 URI。
❷ 应转发到授权服务的请求头。
❷ 需要转发到授权服务的请求头。
将配置同步到网关:
adc sync -f adc.yaml
- 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: forward-auth-plugin-config
spec:
plugins:
- name: forward-auth
config:
uri: http://apisix-gateway.aic.svc.cluster.local/auth
request_headers:
- Authorization
upstream_headers:
- X-User-ID
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
namespace: aic
name: forward-auth-route
spec:
parentRefs:
- name: apisix
rules:
- matches:
- path:
type: Exact
value: /headers
filters:
- type: ExtensionRef
extensionRef:
group: apisix.apache.org
kind: PluginConfig
name: forward-auth-plugin-config
backendRefs:
- name: httpbin-external-domain
port: 80
❶ 授权服务的 URI。使用 Ingress Controller 时,请通过 Kubernetes 服务地址 引用模拟授权服务。
❷ 应转发到授权服务的请求头。
❷ 需要转发到授权服务的请求头。
上述函数实现了以下逻辑:
kubectl apply -f forward-auth-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: ApisixRoute
metadata:
namespace: aic
name: forward-auth-route
spec:
ingressClassName: apisix
http:
- name: forward-auth-route
match:
paths:
- /headers
upstreams:
- name: httpbin-external-domain
plugins:
- name: forward-auth
enable: true
config:
uri: http://apisix-gateway.aic.svc.cluster.local/auth
request_headers:
- Authorization
upstream_headers:
- X-User-ID
❶ 授权服务的 URI。使用 Ingress Controller 时,请通过 Kubernetes 服务地址引用模拟授权服务。
❷ 应转发到授权服务的请求头。
❷ 需要转发到授权服务的请求头。
上述函数实现了以下逻辑:
kubectl apply -f forward-auth-ic.yaml
向路由发送带有授权详情的请求:
curl "http://127.0.0.1:9080/headers" -H 'Authorization: 123'
你应该会看到如下的 HTTP/1.1 200 OK 响应:
{
"headers": {
"Accept": "*/*",
"Authorization": "123",
...
}
}
要验证授权服务设置的 X-User-ID 头是否转发到了上游服务,请发送带有相应授权详情的请求:
curl "http://127.0.0.1:9080/headers" -H 'Authorization: 321'
你应该会看到如下的 HTTP/1.1 200 OK 响应,显示该头已转发到上游:
{
"headers": {
"Accept": "*/*",
"Authorization": "123",
"X-User-ID": "i-am-user",
...
}
}
认证失败时返回指定头给客户端
以下示例演示了如何在路由上配置 forward-auth 以控制客户端对上游资源的访问。当认证失败时,它还会将授权服务返回的特定头传递给客户端。
创建一个启用了 forward-auth 插件的路由:
- Admin API
- ADC
- Ingress Controller
curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"id": "forward-auth-route",
"uri": "/headers",
"plugins": {
"forward-auth": {
"uri": "http://127.0.0.1:9080/auth",
"request_headers": ["Authorization"],
"client_headers": ["X-Forward-Auth"]
}
},
"upstream": {
"nodes": {
"httpbin.org:80": 1
},
"type": "roundrobin"
}
}'
❶ 当认证失败时,将来自授权服务的 X-Forward-Auth 头传回给客户端。
services:
- name: forward-auth-service
routes:
- name: forward-auth-route
uris:
- /headers
plugins:
forward-auth:
uri: http://127.0.0.1:9080/auth
request_headers:
- Authorization
client_headers:
- X-Forward-Auth
upstream:
type: roundrobin
nodes:
- host: httpbin.org
port: 80
weight: 1
❶ 当认证失败时,将来自授权服务的 X-Forward-Auth 头传回给客户端。
将配置同步到网关:
adc sync -f adc.yaml
- 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: forward-auth-plugin-config
spec:
plugins:
- name: forward-auth
config:
uri: http://apisix-gateway.aic.svc.cluster.local/auth
request_headers:
- Authorization
client_headers:
- X-Forward-Auth
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
namespace: aic
name: forward-auth-route
spec:
parentRefs:
- name: apisix
rules:
- matches:
- path:
type: Exact
value: /headers
filters:
- type: ExtensionRef
extensionRef:
group: apisix.apache.org
kind: PluginConfig
name: forward-auth-plugin-config
backendRefs:
- name: httpbin-external-domain
port: 80
❶ 当认证失败时,将来自授权服务的 X-Forward-Auth 头传回给客户端。
上述函数实现了以下逻辑:
kubectl apply -f forward-auth-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: ApisixRoute
metadata:
namespace: aic
name: forward-auth-route
spec:
ingressClassName: apisix
http:
- name: forward-auth-route
match:
paths:
- /headers
upstreams:
- name: httpbin-external-domain
plugins:
- name: forward-auth
enable: true
config:
uri: http://apisix-gateway.aic.svc.cluster.local/auth
request_headers:
- Authorization
client_headers:
- X-Forward-Auth
❶ 当认证失败时,将来自授权服务的 X-Forward-Auth 头传回给客户端。
上述函数实现了以下逻辑:
kubectl apply -f forward-auth-ic.yaml
发送一个不带任何认证信息的请求:
curl -i "http://127.0.0.1:9080/headers"
你应该收到一个 HTTP/1.1 403 Forbidden 响应:
...
X-Forward-Auth: Fail
Server: APISIX/3.x.x
<html>
<head><title>403 Forbidden</title></head>
<body>
<center><h1>403 Forbidden</h1></center>
<hr><center>openresty</center>
<p><em>Powered by <a href="https://apisix.apache.org/">APISIX</a>.</em></p></body>
</html>
基于 POST Body 进行授权
此示例演示了如何配置 forward-auth 插件以根据 POST Body 数据控制访问,将值 作为头传递给授权服务,并在根据 Body 数据授权失败时拒绝请求。
此示例使用内置变量 $post_arg.* 读取请求体参数。APISIX 会从 application/x-www-form-urlencoded、application/json 和 multipart/form-data 请求体中解析 $post_arg.*,因此客户端必须根据实际发送的请求体设置正确的 Content-Type 请求头。详情请参阅内置变量。
请先设置好你的外部授权服务,或者使用 serverless function 插件 创建一个模拟认证服务。该函数检查 tenant_id 头是否为 123,如果是则返回 200 OK,否则返回 403 错误。
- Admin API
- ADC
- Ingress Controller
curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-H 'Content-Type: application/json' \
-d '{
"id": "auth-mock",
"uri": "/auth",
"plugins": {
"serverless-pre-function": {
"phase": "rewrite",
"functions": [
"return function(conf, ctx)
local core = require(\"apisix.core\")
local tenant_id = core.request.header(ctx, \"tenant_id\")
if tenant_id == \"123\" then
core.response.exit(200);
else
core.response.exit(403, \"tenant_id is \"..tenant_id .. \" but expecting 123\");
end
end"
]
}
}
}'
services:
- name: auth-mock-service
routes:
- name: auth-mock-route
uris:
- /auth
plugins:
serverless-pre-function:
phase: rewrite
functions:
- |
return function(conf, ctx)
local core = require("apisix.core")
local tenant_id = core.request.header(ctx, "tenant_id")
if tenant_id == "123" then
core.response.exit(200)
else
core.response.exit(403, "tenant_id is " .. tenant_id .. " but expecting 123")
end
end
upstream:
type: roundrobin
nodes:
- host: httpbin.org
port: 80
weight: 1
将配置同步到网关:
adc sync -f adc-auth-mock.yaml
- 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: auth-mock-plugin-config
spec:
plugins:
- name: serverless-pre-function
config:
phase: rewrite
functions:
- |
return function(conf, ctx)
local core = require("apisix.core")
local tenant_id = core.request.header(ctx, "tenant_id")
if tenant_id == "123" then
core.response.exit(200)
else
core.response.exit(403, "tenant_id is " .. tenant_id .. " but expecting 123")
end
end
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
namespace: aic
name: auth-mock-route
spec:
parentRefs:
- name: apisix
rules:
- matches:
- path:
type: Exact
value: /auth
filters:
- type: ExtensionRef
extensionRef:
group: apisix.apache.org
kind: PluginConfig
name: auth-mock-plugin-config
backendRefs:
- name: httpbin-external-domain
port: 80
上述函数实现了以下逻辑:
kubectl apply -f forward-auth-post-mock-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: ApisixRoute
metadata:
namespace: aic
name: auth-mock-route
spec:
ingressClassName: apisix
http:
- name: auth-mock-route
match:
paths:
- /auth
upstreams:
- name: httpbin-external-domain
plugins:
- name: serverless-pre-function
enable: true
config:
phase: rewrite
functions:
- |
return function(conf, ctx)
local core = require("apisix.core")
local tenant_id = core.request.header(ctx, "tenant_id")
if tenant_id == "123" then
core.response.exit(200)
else
core.response.exit(403, "tenant_id is " .. tenant_id .. " but expecting 123")
end
end
上述函数实现了以下逻辑:
kubectl apply -f forward-auth-post-mock-ic.yaml
创建一个启用了 forward-auth 插件的路由:
- Admin API
- ADC
- Ingress Controller
curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"id": "forward-auth-route",
"uri": "/post",
"methods": ["POST"],
"plugins": {
"forward-auth": {
"uri": "http://127.0.0.1:9080/auth",
"request_method": "GET",
"extra_headers": {"tenant_id": "$post_arg.tenant_id"}
}
},
"upstream": {
"nodes": {
"httpbin.org:80": 1
},
"type": "roundrobin"
}
}'
❶ 使用 POST 参数 tenant_id 的值设置额外的头 tenant_id。
services:
- name: forward-auth-service
routes:
- name: forward-auth-route
uris:
- /post
methods:
- POST
plugins:
forward-auth:
uri: http://127.0.0.1:9080/auth
request_method: GET
extra_headers:
tenant_id: "$post_arg.tenant_id"
upstream:
type: roundrobin
nodes:
- host: httpbin.org
port: 80
weight: 1
❶ 使用 POST 参数 tenant_id 的值设置额外的头 tenant_id。
将配置同步到网关:
adc sync -f adc.yaml
- 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: forward-auth-post-plugin-config
spec:
plugins:
- name: forward-auth
config:
uri: http://apisix-gateway.aic.svc.cluster.local/auth
request_method: GET
extra_headers:
tenant_id: "$post_arg.tenant_id"
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
namespace: aic
name: forward-auth-route
spec:
parentRefs:
- name: apisix
rules:
- matches:
- path:
type: Exact
value: /post
method: POST
filters:
- type: ExtensionRef
extensionRef:
group: apisix.apache.org
kind: PluginConfig
name: forward-auth-post-plugin-config
backendRefs:
- name: httpbin-external-domain
port: 80
❶ 使用 POST 参数 tenant_id 的值设置额外的头 tenant_id。
上述函数实现了以下逻辑:
kubectl apply -f forward-auth-post-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: ApisixRoute
metadata:
namespace: aic
name: forward-auth-route
spec:
ingressClassName: apisix
http:
- name: forward-auth-route
match:
paths:
- /post
methods:
- POST
upstreams:
- name: httpbin-external-domain
plugins:
- name: forward-auth
enable: true
config:
uri: http://apisix-gateway.aic.svc.cluster.local/auth
request_method: GET
extra_headers:
tenant_id: "$post_arg.tenant_id"
❶ 使用 POST 参数 tenant_id 的值设置额外的头 tenant_id。
上述函数实现了以下逻辑:
kubectl apply -f forward-auth-post-ic.yaml
发送一个在 JSON 请求体中包含 tenant_id 的 POST 请求:
curl -i "http://127.0.0.1:9080/post" -X POST \
-H 'Content-Type: application/json' \
-d '{"tenant_id": "123"}'
你应该收到一个 HTTP/1.1 200 OK 响应。
发送一个 Body 中包含错误 tenant_id 的 POST 请求:
curl -i "http://127.0.0.1:9080/post" -X POST \
-H 'Content-Type: application/json' \
-d '{"tenant_id": "000"}'
你应该收到如下的 HTTP/1.1 403 Forbidden 响应:
tenant_id is 000 but expecting 123