🤔 Introducing APISIX AI Gateway – Built for LLMs and AI workloads. Learn More

forward-auth

描述

forward-auth 插件支持与外部授权服务集成,用于身份验证和授权。如果认证失败,将向客户端返回可自定义的错误消息。如果认证成功,请求将连同以下由 APISIX 添加的请求头一起转发到上游服务:

  • X-Forwarded-Proto:协议
  • X-Forwarded-Method:HTTP 方法
  • X-Forwarded-Host:主机
  • X-Forwarded-Uri:URI
  • X-Forwarded-For:源 IP

属性

名称类型必选项默认值有效值描述
uristring外部授权服务的 URI。
ssl_verifybooleantrue如果为 true,则验证授权服务的 SSL 证书。
request_methodstringGETGETPOSTAPISIX 向外部授权服务发送请求时使用的 HTTP 方法。当设置为 POST 时,APISIX 将连同请求体一起向外部授权服务发送 POST 请求。如果授权决策依赖 POST 正文中的请求参数,建议使用 $post_arg.* 提取必要字段并通过 extra_headers 字段传递,而不是发送完整请求体。
request_headersarray需要转发给外部授权服务的客户端请求头。如果未配置,则只转发 APISIX 添加的请求头,例如 X-Forwarded-*
upstream_headersarray认证通过时,需要转发到 Upstream 服务的外部授权服务响应头。如果未配置,则不转发任何请求头。
client_headersarray认证失败时,需要转发给客户端的外部授权服务响应头。如果未配置,则不转发任何响应头。
extra_headersobject发送给授权服务的额外请求头,支持在值中使用 NGINX 变量
timeoutinteger30001 到 60000 之间(含)外部授权服务 HTTP 调用的超时时间(毫秒)。
keepalivebooleantrue如果为 true,则保持连接以处理多个请求。
keepalive_timeoutinteger60000>= 1000已建立的 HTTP 连接在关闭前的空闲时间(毫秒)。
keepalive_poolinteger5>= 1连接池中的最大连接数。
allow_degradationbooleanfalse如果为 true,则允许在插件或其依赖项不可用时,APISIX 继续处理请求而不使用该插件。
status_on_errorinteger403200 到 599 之间(含)与外部授权服务出现网络错误时返回给客户端的 HTTP 状态码。

使用示例

以下示例演示了如何针对不同场景使用 forward-auth

NOTE

你可以这样从 config.yaml 中获取 admin_key 并存入环境变量:

admin_key=$(yq '.deployment.admin.admin_key[0].key' conf/config.yaml | sed 's/"//g')

要跟随前两个示例操作,请提前搭建好外部授权服务,或使用 serverless-pre-function 插件创建如下所示的模拟认证服务:

Admin API

curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
  -H "X-API-KEY: ${admin_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"
        ]
      }
    }
  }'

ADC

adc-auth-mock.yaml
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

将配置同步到网关:

adc sync -f adc-auth-mock.yaml

Ingress Controller

Gateway API

forward-auth-mock-ic.yaml
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

将配置应用到集群:

kubectl apply -f forward-auth-mock-ic.yaml

APISIX Ingress Controller

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

将配置应用到集群:

kubectl apply -f forward-auth-mock-ic.yaml

将指定请求头转发至上游资源

以下示例演示了如何在路由上配置 forward-auth,根据请求头中的值控制客户端对上游资源的访问,并将授权服务设置的特定请求头转发至上游资源。

按如下方式创建带有 forward-auth 插件的路由:

Admin API

curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
  -H "X-API-KEY: ${admin_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"
    }
  }'

ADC

adc.yaml
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

将配置同步到网关:

adc sync -f adc.yaml

Ingress Controller

Gateway API

forward-auth-ic.yaml
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

将配置应用到集群:

kubectl apply -f forward-auth-ic.yaml

APISIX Ingress Controller

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

将配置应用到集群:

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

curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
  -H "X-API-KEY: ${admin_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"
    }
  }'

ADC

adc.yaml
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

将配置同步到网关:

adc sync -f adc.yaml

Ingress Controller

Gateway API

forward-auth-ic.yaml
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

将配置应用到集群:

kubectl apply -f forward-auth-ic.yaml

APISIX Ingress Controller

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

将配置应用到集群:

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 请求体进行授权

本示例演示如何配置 forward-auth 插件,根据 POST 请求体数据控制访问,将值以请求头的形式传递给授权服务,并在授权失败时拒绝请求。

请提前搭建好外部授权服务,或使用 serverless-pre-function 插件创建模拟认证服务。该函数检查 tenant_id 请求头是否为 123,如果是则返回 200 OK,否则返回 403 和错误消息。

Admin API

curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
  -H "X-API-KEY: ${admin_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"
        ]
      }
    }
  }'

ADC

adc-auth-mock.yaml
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
                    local tid = tenant_id or "<missing>"
                    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

Ingress Controller

Gateway API

forward-auth-post-mock-ic.yaml
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
                local tid = tenant_id or "<missing>"
                core.response.exit(403, "tenant_id is " .. tostring(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

APISIX Ingress Controller

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 " .. tostring(tenant_id) .. " but expecting 123")
                end
              end

将配置应用到集群:

kubectl apply -f forward-auth-post-mock-ic.yaml

按如下方式创建带有 forward-auth 插件的路由:

Admin API

curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
  -H "X-API-KEY: ${admin_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"
    }
  }'

ADC

adc.yaml
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

将配置同步到网关:

adc sync -f adc.yaml

Ingress Controller

Gateway API

forward-auth-post-ic.yaml
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

将配置应用到集群:

kubectl apply -f forward-auth-post-ic.yaml

APISIX Ingress Controller

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"

将配置应用到集群:

kubectl apply -f forward-auth-post-ic.yaml

在请求体中携带 tenant_id 发送 POST 请求:

curl -i "http://127.0.0.1:9080/post" -X POST \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d 'tenant_id=123'

你应该收到 HTTP/1.1 200 OK 响应。

在请求体中携带不同的 tenant_id 发送 POST 请求:

curl -i "http://127.0.0.1:9080/post" -X POST \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d 'tenant_id=000'

你应该收到 HTTP/1.1 403 Forbidden 响应,内容如下:

tenant_id is 000 but expecting 123