title: forward-auth keywords:

  • Apache APISIX
  • API 网关
  • Plugin
  • Forward Authentication
  • forward-auth description: 本文介绍了关于 Apache APISIX forward-auth 插件的基本信息及使用方法。

描述

forward-auth 插件使用的是经典外部认证。当身份认证失败时,可以实现自定义错误或者重定向到认证页面的场景。

forward-auth 插件巧妙地将身份认证和授权逻辑移到了一个专门的外部服务中,APISIX 将用户的请求转发给认证服务并阻塞原始请求,然后在认证服务下以非 2xx 状态响应时进行结果替换。

属性

名称类型必选项默认值有效值描述
uristring设置 authorization 服务的地址 (例如:https://localhost:9188)。
ssl_verifybooleantrue[true, false]当设置为 true 时,验证 SSL 证书。
request_methodstringGET[“GET”,“POST”]客户端向 authorization 服务发送请求的方法。当设置为 POST 时,会将 request body 转发至 authorization 服务。
request_headersarray[string]设置需要由客户端转发到 authorization 服务的请求头。如果没有设置,则只发送 APISIX 提供的 headers (例如:X-Forwarded-XXX)。
extra_headersobjectFalse以键值格式传递给授权服务的额外标头。值可以是变量,例如“$request_uri”或“$post_arg.xyz”。
upstream_headersarray[string]认证通过时,设置 authorization 服务转发至 upstream 的请求头。如果不设置则不转发任何请求头。
client_headersarray[string]认证失败时,由 authorization 服务向 client 发送的响应头。如果不设置则不转发任何响应头。
timeoutinteger3000ms[1, 60000]msauthorization 服务请求超时时间。
keepalivebooleantrue[true, false]HTTP 长连接。
keepalive_timeoutinteger60000ms[1000, ...]ms长连接超时时间。
keepalive_poolinteger5[1, ...]ms长连接池大小。
allow_degradationbooleanfalse当设置为 true 时,允许在身份验证服务器不可用时跳过身份验证。
status_on_errorinteger403[200,...,599]设置授权服务出现网络错误时返回给客户端的 HTTP 状态。默认状态为“403”。

数据定义

APISIX 将生成并发送如下所示的请求头到认证服务:

SchemeHTTP MethodHostURISource IP
X-Forwarded-ProtoX-Forwarded-MethodX-Forwarded-HostX-Forwarded-UriX-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"
    }
}
  • 转发认证服务响应头到 Upstream。
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

Using data from POST body to make decision on Authorization service

::: 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
        }
    }
}'