title: forward-auth keywords:
forward-auth
插件的基本信息及使用方法。forward-auth
插件使用的是经典外部认证。当身份认证失败时,可以实现自定义错误或者重定向到认证页面的场景。
forward-auth
插件巧妙地将身份认证和授权逻辑移到了一个专门的外部服务中,APISIX 将用户的请求转发给认证服务并阻塞原始请求,然后在认证服务下以非 2xx 状态响应时进行结果替换。
名称 | 类型 | 必选项 | 默认值 | 有效值 | 描述 |
---|---|---|---|---|---|
uri | string | 是 | 设置 authorization 服务的地址 (例如:https://localhost:9188)。 | ||
ssl_verify | boolean | 否 | true | [true, false] | 当设置为 true 时,验证 SSL 证书。 |
request_method | string | 否 | GET | [“GET”,“POST”] | 客户端向 authorization 服务发送请求的方法。当设置为 POST 时,会将 request body 转发至 authorization 服务。 |
request_headers | array[string] | 否 | 设置需要由客户端转发到 authorization 服务的请求头。如果没有设置,则只发送 APISIX 提供的 headers (例如:X-Forwarded-XXX)。 | ||
extra_headers | object | False | 以键值格式传递给授权服务的额外标头。值可以是变量,例如“$request_uri”或“$post_arg.xyz”。 | ||
upstream_headers | array[string] | 否 | 认证通过时,设置 authorization 服务转发至 upstream 的请求头。如果不设置则不转发任何请求头。 | ||
client_headers | array[string] | 否 | 认证失败时,由 authorization 服务向 client 发送的响应头。如果不设置则不转发任何响应头。 | ||
timeout | integer | 否 | 3000ms | [1, 60000]ms | authorization 服务请求超时时间。 |
keepalive | boolean | 否 | true | [true, false] | HTTP 长连接。 |
keepalive_timeout | integer | 否 | 60000ms | [1000, ...]ms | 长连接超时时间。 |
keepalive_pool | integer | 否 | 5 | [1, ...]ms | 长连接池大小。 |
allow_degradation | boolean | 否 | false | 当设置为 true 时,允许在身份验证服务器不可用时跳过身份验证。 | |
status_on_error | integer | 否 | 403 | [200,...,599] | 设置授权服务出现网络错误时返回给客户端的 HTTP 状态。默认状态为“403”。 |
APISIX 将生成并发送如下所示的请求头到认证服务:
Scheme | HTTP Method | Host | URI | Source IP |
---|---|---|---|---|
X-Forwarded-Proto | X-Forwarded-Method | X-Forwarded-Host | X-Forwarded-Uri | X-Forwarded-For |
首先,你需要设置一个外部认证服务。以下示例使用的是 Apache APISIX 无服务器插件模拟服务:
:::note
您可以这样从 config.yaml
中获取 admin_key
并存入环境变量:
admin_key=$(yq '.deployment.admin.admin_key[0].key' conf/config.yaml | sed 's/"//g')
:::
curl -X PUT 'http://127.0.0.1:9180/apisix/admin/routes/auth' \ -H "X-API-KEY: $admin_key" \ -H 'Content-Type: application/json' \ -d '{ "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(\"Location\", \"http://example.com/auth\"); core.response.exit(403); end end" ] } } }'
现在你可以在指定 Route 上启用 forward-auth
插件:
curl -X PUT 'http://127.0.0.1:9180/apisix/admin/routes/1' \ -H "X-API-KEY: $admin_key" \ -d '{ "uri": "/headers", "plugins": { "forward-auth": { "uri": "http://127.0.0.1:9080/auth", "request_headers": ["Authorization"], "upstream_headers": ["X-User-ID"], "client_headers": ["Location"] } }, "upstream": { "nodes": { "httpbin.org:80": 1 }, "type": "roundrobin" } }'
完成上述配置后,可通过以下三种方式进行测试:
curl http://127.0.0.1:9080/headers -H 'Authorization: 123'
{ "headers": { "Authorization": "123", "Next": "More-headers" } }
curl http://127.0.0.1:9080/headers -H 'Authorization: 321'
{ "headers": { "Authorization": "321", "X-User-ID": "i-am-user", "Next": "More-headers" } }
curl -i http://127.0.0.1:9080/headers
HTTP/1.1 403 Forbidden Location: http://example.com/auth
::: note 当要根据 POST 正文做出决定时,建议使用带有 extra_headers
字段的 $post_arg.*
并根据标头对授权服务做出决定,而不是使用 POST request_method
将整个请求正文传递给授权服务。 :::
在 /auth
路由上创建一个无服务器函数,用于检查 tenant_id
标头是否存在。如果存在,路由会使用 HTTP 200 进行响应,并将 X-User-ID
标头设置为固定值 i-am-an-user
。如果缺少 tenant_id
,则会返回 HTTP 400 和错误消息。
curl -X PUT 'http://127.0.0.1:9180/apisix/admin/routes/auth' \ -H "X-API-KEY: $admin_key" \ -H 'Content-Type: application/json' \ -d '{ "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.set_header(\"X-User-ID\", \"i-am-an-user\"); core.response.exit(200); else core.response.exit(400, \"tenant_id is \"..tenant_id .. \" but expected 123\"); end end" ] } } }'
创建一个接受 POST 请求的路由,并使用 forward-auth
插件通过请求中的 tenant_id
调用身份验证端点。仅当身份验证检查返回 200 时,请求才会转发到上游服务。
curl -X PUT 'http://127.0.0.1:9180/apisix/admin/routes/1' \ -H "X-API-KEY: $admin_key" \ -d '{ "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" } }'
发送带有 tenant_id
标头的 POST 请求:
curl -i http://127.0.0.1:9080/post -H "Content-Type: application/json" -X POST -d '{ "tenant_id": "123" }'
您应该收到类似以下内容的 HTTP/1.1 200 OK
响应:
{ "args": {}, "data": "{\n \"tenant_id\": \"123\"\n}", "files": {}, "form": {}, "headers": { "Accept": "*/*", "Content-Length": "25", "Content-Type": "application/json", "Host": "127.0.0.1", "User-Agent": "curl/8.13.0", "X-Amzn-Trace-Id": "Root=1-687775d8-6890073173b30c2834901e8b", "X-Forwarded-Host": "127.0.0.1" }, "json": { "tenant_id": "123" }, "origin": "127.0.0.1, 106.215.82.114", "url": "http://127.0.0.1/post" }
发送带有错误 tenant_id
标头的 POST 请求:
curl -i http://127.0.0.1:9080/post -H "Content-Type: application/json" -X POST -d '{ "tenant_id": "asdfasd" }'
您应该收到包含以下消息的 HTTP/1.1 400 Bad Request
响应:
tenant_id is asdfasd but expected 123
当你需要禁用 forward-auth
插件时,可以通过以下命令删除相应的 JSON 配置,APISIX 将会自动重新加载相关配置,无需重启服务:
curl http://127.0.0.1:9180/apisix/admin/routes/1 \ -H "X-API-KEY: $admin_key" -X PUT -d ' { "methods": ["GET"], "uri": "/hello", "plugins": {}, "upstream": { "type": "roundrobin", "nodes": { "httpbin.org:80": 1 } } }'