teg Higress鉴权限流插件架构深度解析

葫芦的运维日志

浏览量 5 2026/02/26 17:04

Higress 鉴权限流插件架构深度解析:从原理到生产实战

上一篇文章我们聊了怎么在 EKS 上用 Higress 替换 nginx-ingress,顺带提了一嘴认证授权和限流。但说实话,那篇只是"能用"的程度,离"用好"还差得远。

这篇文章,我们把 Higress 的插件体系拆开来看。不只是贴配置,而是搞清楚:请求进来之后到底经历了什么?插件之间是怎么协作的?为什么有些插件要放在前面,有些要放在后面?

搞懂这些,你才能在生产环境里把鉴权和限流玩出花来,而不是照着文档抄一遍配置然后祈祷它能跑。

一、Higress 插件架构:请求的一生

在聊具体插件之前,先搞清楚 Higress 的插件是怎么运行的。这是理解后面所有内容的基础。

1.1 Wasm 沙箱:插件的安全屋

Higress 的插件跑在 WebAssembly(Wasm)沙箱里。这不是什么花哨的技术选型,而是实打实的工程考量:

  • 插件代码跑在独立的内存空间里,一个插件崩了不会拖垮整个网关
  • 支持 Go、Rust、JS 多语言编写,不像 nginx 的 Lua 插件那样只能用一种语言
  • 热加载,更新插件不需要重启网关进程

用大白话说:Wasm 沙箱就像是给每个插件分了一间独立的办公室,门锁好了,空调独立控制,隔壁同事摔杯子不影响你干活。

1.2 插件执行阶段与优先级

Higress 把请求处理分成了几个阶段,每个插件都有自己的"执行阶段"和"优先级"。这决定了谁先跑、谁后跑。

Higress 插件执行阶段与优先级 客户端请求 认证阶段 优先级 300-400 授权阶段 优先级 200-300 默认阶段 优先级 1-200 后端 认证阶段插件 jwt-auth (优先级 340) key-auth (优先级 320) hmac-auth (优先级 310) basic-auth (优先级 300) ext-auth (优先级 300) 授权阶段插件 ip-restriction (优先级 250) consumer-restriction bot-detect (优先级 210) 默认阶段插件 key-rate-limit (优先级 10) cluster-key-rate-limit request-block header-control 执行顺序规则: 1. 先按阶段排序:认证阶段 -> 授权阶段 -> 默认阶段 2. 同一阶段内,按优先级从高到低执行(数字越大越先执行) 3. 任何阶段的插件返回非 200 响应,后续插件不再执行,直接返回客户端 4. 这意味着:先验身份(你是谁)-> 再查权限(你能干啥)-> 最后管流量(别太猛) * 优先级数字来自 Higress 官方文档,自定义插件可以指定自己的阶段和优先级

 

这个执行顺序非常重要。举个例子:如果你把限流插件放在认证插件前面,那恶意用户可以用大量无效请求把你的限流配额耗光,导致合法用户也被限流。正确的做法是先认证(挡掉无效请求),再限流(保护后端服务)。

 

Higress 的默认优先级已经帮你安排好了这个顺序,但理解背后的逻辑,能帮你在自定义插件时做出正确的决策。

 

1.3 插件配置的三个层级

 

Higress 的插件配置有三个层级,从粗到细:

插件配置的三个层级 全局级别 (Instance Level) 对所有路由和域名生效,适合全站统一策略 域名级别 (Domain Level) 对特定域名下的所有路由生效 路由级别 (Route Level) 对特定路由生效,优先级最高 优先级低 优先级高

关键规则:路由级别 > 域名级别 > 全局级别。如果同一个插件在多个层级都有配置,细粒度的配置会覆盖粗粒度的。

 

一个常见的用法:全局开启 JWT 认证(global_auth: true),但在健康检查路由上单独关闭。这样就不用给每个路由都配一遍认证了。

 

二、鉴权插件深度解析

 

Higress 内置了五种鉴权插件,覆盖了从简单到复杂的各种场景。我们逐个拆解。

 

2.1 JWT Auth:最主流的 API 鉴权方案

 

JWT(JSON Web Token)是目前最流行的无状态认证方案。Higress 的 jwt-auth 插件不只是验证 Token,还能识别调用者身份,支持不同调用者使用不同的 JWT 凭证。

JWT 认证完整流程 1. 客户端 携带 JWT Token 2. Higress 网关 jwt-auth 插件拦截 3. 验证 Token JWKS / 对称密钥 验证通过 -> 转发后端 验证失败 -> 返回 401 验证通过后,Higress 做了什么? 1. 从 Token 的 iss 字段匹配到对应的 Consumer(调用者身份) 2. 在请求头中注入 X-Mse-Consumer: consumer_name(后端可以直接读取调用者身份) 3. 根据 claims_to_headers 配置,把 Token 中的字段提取到请求头(如 X-User-Id, X-User-Role) 4. 检查路由级别的 allow 列表,确认该 Consumer 是否有权访问当前路由 5. 根据 keep_token 配置决定是否保留原始 Token 转发给后端(默认保留) * 如果 Consumer 不在 allow 列表中,返回 403 Forbidden(注意不是 401)

来看一个生产级的 JWT 配置,比上篇文章的示例更完整:

 

# WasmPlugin CRD 方式配置 jwt-auth(推荐)
apiVersion: extensions.higress.io/v1alpha1
kind: WasmPlugin
metadata:
  name: jwt-auth
  namespace: higress-system
spec:
  defaultConfig:
    # 全局认证开关:false 表示只对配置了 allow 的路由生效
    global_auth: false
    consumers:
      # Consumer 1:移动端 App,用 RSA 非对称密钥
      - name: mobile-app
        issuer: "https://auth.example.com"
        jwks: |
          {
            "keys": [
              {
                "kty": "RSA",
                "e": "AQAB",
                "use": "sig",
                "kid": "mobile-app-key-2025",
                "alg": "RS256",
                "n": "你的RSA公钥N值..."
              }
            ]
          }
        # 从 Authorization: Bearer xxx 提取 Token
        from_headers:
          - name: Authorization
            value_prefix: "Bearer "
        # 把 Token 中的字段透传给后端
        claims_to_headers:
          - claim: sub
            header: X-User-Id
          - claim: role
            header: X-User-Role
          - claim: tenant_id
            header: X-Tenant-Id
        # 允许 5 秒的时钟偏差(分布式系统时钟不完全同步)
        clock_skew_seconds: 5
        # 转发给后端时保留原始 Token
        keep_token: true

      # Consumer 2:内部微服务,用 HMAC 对称密钥(简单高效)
      - name: internal-service
        issuer: "higress-internal"
        jwks: |
          {
            "keys": [
              {
                "kty": "oct",
                "kid": "internal-key-v1",
                "k": "你的256位对称密钥Base64编码...",
                "alg": "HS256"
              }
            ]
          }
        from_headers:
          - name: X-Internal-Token
            value_prefix: ""

      # Consumer 3:第三方合作伙伴,从 URL 参数提取 Token
      - name: partner-sdk
        issuer: "partner-auth"
        jwks: |
          {
            "keys": [
              {
                "kty": "RSA",
                "e": "AQAB",
                "use": "sig",
                "kid": "partner-key-v1",
                "alg": "RS256",
                "n": "合作伙伴的RSA公钥N值..."
              }
            ]
          }
        # 从 URL 参数 token 提取
        from_params:
          - token
        # 也支持从 Cookie 提取(Web SDK 场景)
        from_cookies:
          - session_token

 

# 路由级别授权:只允许特定 Consumer 访问
# 用户 API:只允许移动端和合作伙伴
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: user-api
  annotations:
    higress.io/jwt-auth: |
      allow:
        - mobile-app
        - partner-sdk
spec:
  ingressClassName: higress
  rules:
    - host: api.example.com
      http:
        paths:
          - path: /api/v1/users
            pathType: Prefix
            backend:
              service:
                name: user-service
                port:
                  number: 80
---
# 内部管理 API:只允许内部服务
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: admin-api
  annotations:
    higress.io/jwt-auth: |
      allow:
        - internal-service
spec:
  ingressClassName: higress
  rules:
    - host: api.example.com
      http:
        paths:
          - path: /api/internal
            pathType: Prefix
            backend:
              service:
                name: admin-service
                port:
                  number: 80

 

几个关键细节:

 

  • RSA vs HMAC:对外用 RSA(公钥验证,私钥不需要放在网关上),对内用 HMAC(性能更好,但密钥需要双方共享)
  • clock_skew_seconds:分布式系统的时钟不可能完全同步,给 5 秒的容差是合理的
  • global_auth: false:不要轻易设为 true,否则健康检查、公开页面等都需要带 Token
  • 多 Consumer 匹配:如果一个 JWT 能匹配多个 Consumer 的 JWKS,按配置顺序取第一个匹配的

 

2.2 Key Auth:简单粗暴但好用

 

不是所有场景都需要 JWT 那么复杂的认证。对接第三方、开放 API、内部工具调用,一个 API Key 就够了。

 

# key-auth 插件配置
apiVersion: extensions.higress.io/v1alpha1
kind: WasmPlugin
metadata:
  name: key-auth
  namespace: higress-system
spec:
  defaultConfig:
    global_auth: false
    consumers:
      - name: data-team
        credential: "dk-2025-data-team-a1b2c3d4e5f6"
      - name: frontend-bff
        credential: "dk-2025-frontend-bff-x7y8z9"
      - name: partner-webhook
        credential: "dk-2025-partner-wh-m3n4o5"
    # 从请求头提取 API Key
    keys:
      - name: X-API-Key
        in: header
    # 也可以从 URL 参数提取(不推荐,会出现在日志和浏览器历史中)
    # keys:
    #   - name: api_key
    #     in: query

 

Key Auth 的优势是简单:生成一个随机字符串,给到调用方,完事。缺点也很明显:密钥泄露了只能换新的,没有过期时间的概念。适合内部系统和可信合作伙伴,不适合面向公众的 API。

 

2.3 HMAC Auth:防篡改的签名认证

 

如果你需要比 API Key 更安全,但又不想上 JWT 那么重的方案,HMAC 签名认证是个好选择。它不只验证身份,还能防止请求被篡改。

HMAC 签名认证流程 客户端(签名生成) 1. 构造签名字符串: GET\n/api/v1/users\n x-date:Thu, 22 Feb 2025 10:00:00 GMT 2. 用 Secret Key 做 HMAC-SHA256 签名 3. 把签名放到请求头: Authorization: hmac username="app", algorithm="hmac-sha256", signature="Base64(签名结果)" 4. 发送请求 HTTPS Higress 网关(签名验证) 1. 从请求头提取 username 2. 查找对应 Consumer 的 Secret 3. 用相同方式构造签名字符串 4. 用 Secret 计算 HMAC 签名 5. 对比计算结果和请求中的签名 匹配 -> 放行(身份确认+内容未篡改) 不匹配 -> 拒绝(可能被篡改)

# hmac-auth 插件配置(兼容 APISIX 格式)
apiVersion: extensions.higress.io/v1alpha1
kind: WasmPlugin
metadata:
  name: hmac-auth
  namespace: higress-system
spec:
  defaultConfig:
    consumers:
      - name: partner-payment
        key: "payment-app-id-001"
        secret: "a]随机生成的256位密钥..."
        # 签名中必须包含的请求头
        signed_headers:
          - x-date
          - x-request-id
        # 签名算法
        algorithm: "hmac-sha256"
    # 签名有效期(防重放攻击)
    date_offset: 300  # 签名时间戳与服务器时间差不超过 5 分钟

 

HMAC 认证的核心优势:防篡改。即使攻击者截获了请求,修改任何一个被签名的字段(URL、请求头、Body),签名就会对不上。再加上时间戳校验,还能防止重放攻击。支付回调、金融接口这类对安全性要求高的场景,HMAC 是标配。

 

2.4 Ext Auth:接入你自己的认证服务

 

如果你已经有一套成熟的认证体系(比如 Keycloak、Auth0、自研的用户中心),不想把认证逻辑迁移到网关插件里,ext-auth 就是你的菜。

Ext Auth 外部认证流程 客户端 1. 请求 Higress 网关 ext-auth 插件 2. 转发验证 认证服务 Keycloak / 自研 3. 结果 200 OK 后端服务 4. 转发(带注入的头) 非200 直接返回客户端

# ext-auth 插件配置
apiVersion: extensions.higress.io/v1alpha1
kind: WasmPlugin
metadata:
  name: ext-auth
  namespace: higress-system
spec:
  defaultConfig:
    http_service:
      # 认证服务的地址(K8s 内部服务)
      endpoint_url: "http://auth-service.auth.svc.cluster.local:8080/verify"
      # 超时设置:认证服务挂了不能拖垮整个网关
      timeout: 1000  # 1 秒超时
      # 转发给认证服务的请求头(只转发必要的,不要全转发)
      authorization_request:
        allowed_headers:
          - Authorization
          - Cookie
          - X-Forwarded-For
          - X-Request-Id
        # 额外添加的请求头
        headers_to_add:
          X-Auth-Source: "higress-gateway"
      # 认证服务返回的头,透传给后端
      authorization_response:
        allowed_upstream_headers:
          - X-User-Id
          - X-User-Role
          - X-Tenant-Id
          - X-User-Permissions
        # 认证服务返回的头,透传给客户端
        allowed_client_headers:
          - X-Auth-Error
          - WWW-Authenticate

 

Ext Auth 的精髓在于解耦:认证逻辑完全由你的认证服务控制,网关只负责"问一嘴"和"传话"。认证服务返回 200 就放行,返回其他状态码就拒绝。这意味着你可以在认证服务里实现任意复杂的逻辑:多因素认证、设备指纹、风控规则,网关完全不需要改。

 

2.5 鉴权插件选型指南

 

场景 推荐插件 理由
移动端 App / SPA 前端 jwt-auth (RSA) 无状态、公钥验证、支持密钥轮转
内部微服务间调用 jwt-auth (HMAC) 或 key-auth 性能好、配置简单
第三方合作伙伴 API key-auth 或 hmac-auth Key Auth 简单对接,HMAC 防篡改
支付/金融类接口 hmac-auth 签名防篡改 + 时间戳防重放
已有认证体系(SSO/IdP) ext-auth 不侵入现有架构,认证逻辑可控
多种认证方式并存 组合使用 不同路由配不同插件,互不干扰

 

三、限流插件深度解析

 

鉴权解决的是"你是谁"和"你能不能进来"的问题。限流解决的是"进来之后别太猛"的问题。Higress 提供了两种限流插件,覆盖从单机到集群的场景。

 

3.1 本地限流 vs 集群限流:先搞清楚区别

本地限流 vs 集群限流 本地限流 (key-rate-limit) Gateway Pod 1 计数器: 45/100 Gateway Pod 2 计数器: 62/100 Gateway Pod 3 计数器: 38/100 每个 Pod 独立计数 配置 100 QPS,3 个 Pod 实际总限流 = 300 QPS 优点:零延迟、无外部依赖 缺点:总限流随 Pod 数量变化 适合:粗粒度保护、突发流量防护 集群限流 (cluster-key-rate-limit) Gateway Pod 1 Gateway Pod 2 Gateway Pod 3 Redis 全局计数: 145/100 所有 Pod 共享 Redis 计数 实际总限流 = 精确 100 QPS 优点:精确控制、不受 Pod 数量影响 缺点:依赖 Redis、有网络延迟

选哪个?简单原则:

 

  • 保护后端不被打垮 -> 本地限流就够了,简单可靠
  • 精确控制 API 调用配额(比如免费用户每天 1000 次)-> 必须用集群限流
  • 两个都用 -> 本地限流做粗粒度保护(防 DDoS),集群限流做细粒度配额管理

 

3.2 本地限流实战:key-rate-limit

 

key-rate-limit 插件基于特定键值做限流,键值可以来自 URL 参数或请求头。每个 Gateway Pod 独立计数,不需要外部依赖。

 

# 场景 1:按 API Key 区分限流(不同客户不同配额)
apiVersion: extensions.higress.io/v1alpha1
kind: WasmPlugin
metadata:
  name: key-rate-limit-by-apikey
  namespace: higress-system
spec:
  defaultConfig:
    # 从 URL 参数 apikey 提取限流键值
    limit_by_param: apikey
    limit_keys:
      # 付费客户:每秒 50 次
      - key: "premium-client-key-001"
        query_per_second: 50
      # 免费客户:每分钟 100 次
      - key: "free-client-key-002"
        query_per_minute: 100
      # 内部测试:每秒 200 次(不限制)
      - key: "internal-test-key"
        query_per_second: 200

 

# 场景 2:按请求头区分限流(多租户场景)
apiVersion: extensions.higress.io/v1alpha1
kind: WasmPlugin
metadata:
  name: key-rate-limit-by-tenant
  namespace: higress-system
spec:
  defaultConfig:
    # 从请求头 X-Tenant-Id 提取限流键值
    limit_by_header: x-tenant-id
    limit_keys:
      # 大客户:每秒 1000 次
      - key: "tenant-enterprise-001"
        query_per_second: 1000
      # 中型客户:每秒 200 次
      - key: "tenant-standard-002"
        query_per_second: 200
      # 试用客户:每小时 500 次
      - key: "tenant-trial-003"
        query_per_hour: 500

 

本地限流的配置非常直观:指定从哪里提取 Key,然后给每个 Key 设置配额。支持四种时间窗口:query_per_secondquery_per_minutequery_per_hourquery_per_day

 

3.3 集群限流实战:cluster-key-rate-limit

 

集群限流基于 Redis 实现全局计数,所有 Gateway Pod 共享同一个计数器。配置比本地限流复杂一些,但功能强大得多。

集群限流 Redis Key 结构 Redis Key 格式:rule_name:rate_limit_type:key_name:key_value 示例 1(按 API Key 限流): routeA-limit:limit_by_param:apikey:premium-client-001 -> 当前计数: 42 routeA-limit:limit_by_param:apikey:free-client-002 -> 当前计数: 89 示例 2(按 IP 限流): routeA-limit:limit_by_per_ip:from-header-xff:1.2.3.4 -> 当前计数: 156 routeA-limit:limit_by_per_ip:from-header-xff:5.6.7.8 -> 当前计数: 23 示例 3(按 Consumer 限流): routeA-limit:limit_by_consumer::mobile-app -> 当前计数: 890 routeA-limit:limit_by_consumer::partner-sdk -> 当前计数: 234

# 生产级集群限流配置:多维度组合
apiVersion: extensions.higress.io/v1alpha1
kind: WasmPlugin
metadata:
  name: cluster-rate-limit
  namespace: higress-system
spec:
  defaultConfig:
    rule_name: "api-v1-rate-limit"
    # 显示限流配额信息(方便客户端自适应)
    show_limit_quota_header: true
    # 被限流时返回 429 和 JSON 错误信息
    rejected_code: 429
    rejected_msg: '{"code":429,"message":"Rate limit exceeded. Please retry later."}'
    rule_items:
      # 规则 1:按 Consumer 精确限流
      - limit_by_consumer: ""
        limit_keys:
          - key: "mobile-app"
            query_per_second: 500      # 移动端:500 QPS
          - key: "partner-sdk"
            query_per_minute: 3000     # 合作伙伴:3000 QPM
          - key: "internal-service"
            query_per_second: 2000     # 内部服务:2000 QPS

      # 规则 2:按 Consumer 正则匹配(兜底)
      - limit_by_per_consumer: ""
        limit_keys:
          - key: "regexp:^premium-.*"
            query_per_second: 100      # premium 开头的:100 QPS
          - key: "regexp:^free-.*"
            query_per_hour: 1000       # free 开头的:1000 QPH
          - key: "*"
            query_per_minute: 60       # 其他所有:60 QPM

      # 规则 3:按客户端 IP 限流(防爬虫/DDoS)
      - limit_by_per_ip: "from-header-x-forwarded-for"
        limit_keys:
          - key: "10.0.0.0/8"
            query_per_second: 1000     # 内网 IP:宽松
          - key: "0.0.0.0/0"
            query_per_second: 50       # 外网 IP:严格

    # Redis 配置
    redis:
      service_name: "redis.redis-system.svc.cluster.local"
      service_port: 6379
      password: "your-redis-password"
      timeout: 500    # 500ms 超时
      database: 1     # 使用 DB 1,和业务数据隔离

 

几个关键点:

 

  • show_limit_quota_header: true:响应头会包含 X-RateLimit-LimitX-RateLimit-Remaining,客户端可以据此做自适应限流
  • rule_items 按顺序匹配:命中第一条就停止,后面的不再检查。所以精确匹配放前面,正则/通配符放后面
  • Redis 超时要设短:Redis 挂了不能拖垮网关。500ms 超时,超时后放行(fail-open),总比全部拒绝好
  • 用独立的 Redis DB:限流数据和业务数据隔离,互不影响

 

3.4 限流 + 鉴权联动:基于身份的差异化限流

 

这是 Higress 插件体系最强大的地方:鉴权插件识别出调用者身份后,限流插件可以直接基于这个身份做差异化限流。不需要额外的代码或配置,插件之间通过 X-Mse-Consumer 请求头自动传递身份信息。

鉴权 + 限流联动流程 1. 请求进入 Bearer eyJhbG... 2. jwt-auth 验证 识别: mobile-app 3. 注入身份头 X-Mse-Consumer: mobile-app 4. 限流检查 mobile-app: 500 QPS 当前: 234 -> 放行 不同身份,不同配额: mobile-app (移动端) -> 500 QPS (高频交互,配额宽松) partner-sdk (合作伙伴) -> 3000 QPM (批量调用,按分钟计) free-trial (试用用户) -> 1000 QPD (每天 1000 次,超了请付费)

这个联动是自动的,不需要你写任何胶水代码。只要:

 

  1. 鉴权插件(jwt-auth / key-auth)识别出 Consumer 名称
  2. 限流插件(cluster-key-rate-limit)配置 limit_by_consumerlimit_by_per_consumer

 

两个插件就能无缝协作。这就是 Higress 插件体系设计的精妙之处:每个插件只做一件事,但通过标准化的请求头传递信息,实现了强大的组合能力。

 

四、IP 管控与 Bot 检测

 

鉴权管的是"合法用户",限流管的是"别太猛"。但还有一类流量,根本不该进来:恶意 IP 和自动化爬虫。

 

4.1 IP 黑白名单:ip-restriction

 

# 场景 1:管理后台只允许办公网络访问
apiVersion: extensions.higress.io/v1alpha1
kind: WasmPlugin
metadata:
  name: ip-restriction-admin
  namespace: higress-system
spec:
  defaultConfig:
    # 从 X-Forwarded-For 获取真实 IP(经过 ELB 转发后必须这样配)
    ip_source_type: header
    ip_header_name: X-Forwarded-For
    # 白名单模式
    allow:
      - "10.0.0.0/8"         # 公司内网
      - "172.16.0.0/12"      # VPN 网段
      - "203.0.113.50"       # 办公室出口 IP
      - "198.51.100.0/24"    # 远程办公 IP 段

 

# 场景 2:封禁恶意 IP(黑名单模式)
apiVersion: extensions.higress.io/v1alpha1
kind: WasmPlugin
metadata:
  name: ip-restriction-block
  namespace: higress-system
spec:
  defaultConfig:
    ip_source_type: header
    ip_header_name: X-Forwarded-For
    deny:
      - "192.0.2.0/24"       # 已知攻击源
      - "198.51.100.100"     # 恶意爬虫 IP
      - "203.0.113.0/28"     # 扫描器 IP 段

 

一个容易踩的坑:在 AWS EKS 环境中,客户端请求经过 NLB/ALB 转发后,源 IP 变成了负载均衡器的内网 IP。真实客户端 IP 在 X-Forwarded-For 请求头里。如果你用默认的 ip_source_type: origin,拿到的是 ELB 的 IP,白名单就形同虚设了。一定要设置 ip_source_type: header

 

4.2 Bot 检测:bot-detect

 

Bot 检测基于 User-Agent 识别自动化工具和爬虫。虽然 User-Agent 可以伪造,但对于大部分低级爬虫和扫描器来说,这一层过滤已经够用了。

 

# bot-detect 插件配置
apiVersion: extensions.higress.io/v1alpha1
kind: WasmPlugin
metadata:
  name: bot-detect
  namespace: higress-system
spec:
  defaultConfig:
    # 拦截规则(正则匹配 User-Agent)
    block_rules:
      # 常见安全扫描工具
      - regex: "(sqlmap|nikto|nmap|masscan|zgrab|nuclei)"
      # 自动化测试工具(生产环境不该出现)
      - regex: "(phantomjs|headless|selenium|puppeteer|playwright)"
      # 恶意爬虫框架
      - regex: "(scrapy|python-requests|go-http-client|java/|okhttp)"
      # 空 User-Agent(正常浏览器不会这样)
      - regex: "^$"
    # 放行规则(优先级高于拦截规则)
    allow_rules:
      # 搜索引擎爬虫(SEO 命脉,必须放行)
      - regex: "(Googlebot|Bingbot|baiduspider|YandexBot|DuckDuckBot)"
      # 社交媒体预览(分享链接时需要抓取 OG 标签)
      - regex: "(facebookexternalhit|Twitterbot|LinkedInBot|WhatsApp)"
      # 监控工具(你自己的健康检查)
      - regex: "(UptimeRobot|Pingdom|Site24x7|Datadog)"

 

注意 allow_rules 的优先级高于 block_rules。这意味着即使 Googlebot 的 User-Agent 里包含了某些被 block 的关键词,只要它匹配了 allow 规则,就会被放行。

 

一个实用建议:不要拦截 curl。很多运维工具和健康检查都用 curl,拦了它你会收到一堆误报告警。

 

五、生产环境完整配置:把所有插件串起来

 

前面讲了一堆单个插件的配置,现在把它们组合起来,看看一个生产环境的完整防护体系长什么样。

生产环境完整防护链 客户端请求 第 1 层:Bot 检测 (bot-detect) 拦截扫描器、恶意爬虫、空 UA | 放行搜索引擎 命中 -> 403 第 2 层:IP 管控 (ip-restriction) 黑名单封禁恶意 IP | 白名单保护管理后台 命中黑名单 -> 403 第 3 层:身份认证 (jwt-auth / key-auth) 验证 Token/Key | 识别 Consumer | 注入身份头 无效凭证 -> 401/403 第 4 层:限流 (cluster-key-rate-limit) 基于 Consumer 身份差异化限流 | IP 级别防刷 超配额 -> 429 第 5 层:请求/响应头控制 (header-control) 注入安全头 | 删除敏感信息 | 添加追踪 ID 后端服务

这五层防护的执行顺序是固定的,由 Higress 的插件阶段和优先级保证。每一层都是一道关卡,请求必须全部通过才能到达后端。任何一层拒绝,后续层都不会执行,直接返回错误响应。

 

下面是一个完整的生产配置示例,把所有插件串在一起:

 

# === 第 1 层:Bot 检测 ===
apiVersion: extensions.higress.io/v1alpha1
kind: WasmPlugin
metadata:
  name: bot-detect
  namespace: higress-system
spec:
  defaultConfig:
    block_rules:
      - regex: "(sqlmap|nikto|nmap|masscan|zgrab|nuclei)"
      - regex: "(scrapy|python-requests|go-http-client)"
      - regex: "^$"
    allow_rules:
      - regex: "(Googlebot|Bingbot|baiduspider)"
      - regex: "(UptimeRobot|Pingdom)"
---
# === 第 2 层:IP 管控 ===
apiVersion: extensions.higress.io/v1alpha1
kind: WasmPlugin
metadata:
  name: ip-restriction
  namespace: higress-system
spec:
  defaultConfig:
    ip_source_type: header
    ip_header_name: X-Forwarded-For
    deny:
      - "192.0.2.0/24"
---
# === 第 3 层:JWT 认证 ===
apiVersion: extensions.higress.io/v1alpha1
kind: WasmPlugin
metadata:
  name: jwt-auth
  namespace: higress-system
spec:
  defaultConfig:
    global_auth: false
    consumers:
      - name: mobile-app
        issuer: "https://auth.example.com"
        jwks: |
          {"keys":[{"kty":"RSA","e":"AQAB","kid":"v1","alg":"RS256","n":"..."}]}
        from_headers:
          - name: Authorization
            value_prefix: "Bearer "
        claims_to_headers:
          - claim: sub
            header: X-User-Id
          - claim: role
            header: X-User-Role
      - name: partner-sdk
        issuer: "partner-auth"
        jwks: |
          {"keys":[{"kty":"RSA","e":"AQAB","kid":"p1","alg":"RS256","n":"..."}]}
        from_headers:
          - name: X-Partner-Token
            value_prefix: ""
---
# === 第 4 层:集群限流 ===
apiVersion: extensions.higress.io/v1alpha1
kind: WasmPlugin
metadata:
  name: cluster-key-rate-limit
  namespace: higress-system
spec:
  defaultConfig:
    rule_name: "production-rate-limit"
    show_limit_quota_header: true
    rejected_code: 429
    rejected_msg: '{"error":"rate_limit_exceeded","retry_after":60}'
    rule_items:
      - limit_by_per_consumer: ""
        limit_keys:
          - key: "mobile-app"
            query_per_second: 500
          - key: "partner-sdk"
            query_per_minute: 3000
          - key: "*"
            query_per_minute: 60
      - limit_by_per_ip: "from-header-x-forwarded-for"
        limit_keys:
          - key: "10.0.0.0/8"
            query_per_second: 1000
          - key: "0.0.0.0/0"
            query_per_second: 50
    redis:
      service_name: "redis.redis-system.svc.cluster.local"
      service_port: 6379
      timeout: 500

 

六、自定义 Wasm 插件:当内置插件不够用时

 

Higress 内置了几十个插件,覆盖了大部分场景。但总有些需求是内置插件搞不定的,比如:

 

  • 根据请求 Body 中的某个字段做限流
  • 调用外部风控系统做实时决策
  • 自定义的 Token 格式(不是标准 JWT)

 

这时候就需要自己写 Wasm 插件了。Higress 支持用 Go、Rust、JS 编写插件,其中 Go 是最推荐的(生态好、上手快)。

 

// 一个简单的自定义限流插件示例(Go)
// 根据请求 Body 中的 user_level 字段做差异化限流
package main

import (
    "github.com/alibaba/higress/plugins/wasm-go/pkg/wrapper"
    "github.com/higress-group/proxy-wasm-go-sdk/proxywasm"
    "github.com/tidwall/gjson"
)

func main() {
    wrapper.SetCtx(
        "custom-body-rate-limit",
        wrapper.ParseConfigBy(parseConfig),
        wrapper.ProcessRequestHeadersBy(onHttpRequestHeaders),
        wrapper.ProcessRequestBodyBy(onHttpRequestBody),
    )
}

type Config struct {
    VipQPS     int64 `json:"vip_qps"`
    NormalQPS  int64 `json:"normal_qps"`
}

func parseConfig(json gjson.Result, config *Config, log wrapper.Log) error {
    config.VipQPS = json.Get("vip_qps").Int()
    config.NormalQPS = json.Get("normal_qps").Int()
    return nil
}

func onHttpRequestHeaders(ctx wrapper.HttpContext, config Config, log wrapper.Log) types.Action {
    // 需要读取 Body,告诉 Envoy 先缓存
    proxywasm.RemoveHttpRequestHeader("content-length")
    return types.ActionContinue
}

func onHttpRequestBody(ctx wrapper.HttpContext, config Config, body []byte, log wrapper.Log) types.Action {
    // 从 Body 中提取 user_level
    userLevel := gjson.GetBytes(body, "user_level").String()
    
    // 根据用户等级设置不同的限流标记
    if userLevel == "vip" {
        proxywasm.AddHttpRequestHeader("X-Rate-Limit-Key", "vip")
    } else {
        proxywasm.AddHttpRequestHeader("X-Rate-Limit-Key", "normal")
    }
    
    return types.ActionContinue
}

 

Go 1.24 已经原生支持编译 Wasm,不再需要 TinyGo。编译命令:

 

# 编译 Wasm 插件
GOOS=wasip1 GOARCH=wasm go build -o plugin.wasm main.go

# 构建 OCI 镜像(Higress 从 OCI 镜像加载插件)
docker build -t your-registry/custom-plugin:v1 .
docker push your-registry/custom-plugin:v1

 

# 部署自定义插件
apiVersion: extensions.higress.io/v1alpha1
kind: WasmPlugin
metadata:
  name: custom-body-rate-limit
  namespace: higress-system
spec:
  url: oci://your-registry/custom-plugin:v1
  # 指定执行阶段和优先级
  phase: DEFAULT
  priority: 5  # 在内置限流插件之后执行
  defaultConfig:
    vip_qps: 1000
    normal_qps: 100

 

自定义插件的开发门槛不高,但有几个注意事项:

 

  • 内存限制:Wasm 沙箱默认内存有限,不要在插件里做大量数据处理
  • 网络调用:插件可以通过 wrapper.HttpCall 调用外部服务,但要注意超时设置
  • 状态管理:插件实例之间不共享状态,需要共享状态请用 Redis 或 SharedData API

 

七、踩坑指南与最佳实践

 

最后分享几个在生产环境中踩过的坑,帮你少走弯路。

 

坑 1:限流配额和 Gateway 副本数的关系

 

本地限流(key-rate-limit)是每个 Pod 独立计数的。如果你配了 100 QPS,跑了 3 个 Pod,实际总限流是 300 QPS。HPA 扩缩容时,总限流会跟着变。

 

解决方案:要么用集群限流(cluster-key-rate-limit),要么在配置时除以预期的 Pod 数量。

 

坑 2:Redis 挂了,集群限流怎么办?

 

默认行为是 fail-open(Redis 不可用时放行所有请求)。这在大多数场景下是合理的:宁可暂时不限流,也不能因为 Redis 故障导致所有请求被拒绝。

 

但如果你的场景对限流精度要求极高(比如计费相关),需要额外的兜底方案:

 

  • Redis 用主从 + Sentinel 或 Redis Cluster 保证高可用
  • 同时配置本地限流作为兜底,设一个比较宽松的阈值

 

坑 3:JWT 时钟偏差导致间歇性 401

 

分布式系统的时钟不可能完全同步。如果 Token 签发服务器的时间比网关快几秒,刚签发的 Token 可能因为 iat(签发时间)在"未来"而被拒绝。

 

解决方案:设置 clock_skew_seconds: 5,允许 5 秒的时钟偏差。同时确保所有服务器都配置了 NTP 时间同步。

 

坑 4:X-Forwarded-For 伪造

 

客户端可以自己设置 X-Forwarded-For 请求头来伪造 IP。在 EKS 环境中,NLB 会把真实客户端 IP 追加到 X-Forwarded-For 的末尾。所以 IP 限制插件应该取最后一个 IP(NLB 追加的),而不是第一个(可能是伪造的)。

 

Higress 的 ip-restriction 插件默认取第一个 IP。如果你的架构是 Client -> NLB -> Higress,需要注意这个行为,必要时在 NLB 层面做 IP 过滤。

 

坑 5:插件配置更新的生效时间

 

Higress 的插件配置通过 xDS 协议推送,通常在几秒内生效。但如果你同时更新了大量插件配置,可能会有短暂的不一致窗口(部分 Pod 已更新,部分还没有)。

 

建议:重要的配置变更在低峰期操作,更新后观察几分钟确认所有 Pod 都已生效。

 

最佳实践清单

 

实践 说明
先认证后限流 利用 Higress 默认的插件优先级,不要手动调整
限流配额留余量 配置值设为预期峰值的 1.5-2 倍,避免正常流量被误杀
集群限流 + 本地限流双保险 集群限流做精确配额,本地限流做粗粒度兜底
Redis 高可用 集群限流依赖 Redis,务必用主从 + Sentinel
监控限流指标 关注 429 响应比例,过高说明配额太紧或有攻击
JWT 密钥定期轮转 RSA 密钥建议每 90 天轮转,JWKS 支持多 kid 平滑过渡
不要拦截搜索引擎 Bot 检测的 allow_rules 里加上 Googlebot 等
日志记录被拒请求 方便事后分析攻击模式和误杀情况

 

总结

 

这篇文章从 Higress 的插件架构讲起,深入拆解了鉴权和限流两大核心能力:

 

模块 核心插件 适用场景
插件架构 Wasm 沙箱 + 三阶段执行 理解插件协作机制
身份认证 jwt-auth / key-auth / hmac-auth / ext-auth 从简单到复杂的认证需求
流量限制 key-rate-limit / cluster-key-rate-limit 单机限流 / 全局精确限流
访问控制 ip-restriction / bot-detect IP 黑白名单 / 爬虫拦截
鉴权+限流联动 Consumer 身份透传 基于身份的差异化配额
自定义扩展 Go Wasm 插件 内置插件无法满足的场景

 

Higress 的插件体系设计得很克制:每个插件只做一件事,但通过标准化的接口(请求头、Consumer 身份)实现了强大的组合能力。这种"Unix 哲学"式的设计,让你可以像搭积木一样组合出复杂的安全策略。

 

记住一个核心原则:先挡坏人(Bot 检测 + IP 管控),再验身份(鉴权),最后管流量(限流)。这个顺序不是随便定的,而是经过无数生产事故验证出来的最佳实践。

我的博客即将同步至腾讯云开发者社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=xdbp05fgqmd

📝 版权声明

本文作者:王梓 | 原文链接:https://www.bthlt.com/note/402-Higress鉴权限流插件架构深度解析

出处:葫芦的运维日志 | 转载请注明出处并保留原文链接

打赏

留言板

留言提交后需管理员审核通过才会显示

© 冰糖葫芦甜(bthlt.com) 2025 王梓打赏联系方式陕ICP备17005322号-1