<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Higress on WYF 的技术博客</title><link>https://saflyer.cn/series/higress/</link><description>Recent content in Higress on WYF 的技术博客</description><generator>Hugo -- gohugo.io</generator><language>zh-cn</language><lastBuildDate>Thu, 17 Sep 2026 22:15:51 +0800</lastBuildDate><atom:link href="https://saflyer.cn/series/higress/index.xml" rel="self" type="application/rss+xml"/><item><title>Higress 01：key-auth 插件本地 Docker 部署与运行机制指南</title><link>https://saflyer.cn/posts/higress-keyauth-docker-deploy/</link><pubDate>Thu, 17 Sep 2026 22:15:51 +0800</pubDate><guid>https://saflyer.cn/posts/higress-keyauth-docker-deploy/</guid><description>&lt;img src="https://saflyer.cn/posts/higress-keyauth-docker-deploy/cover.webp" alt="Featured image of post Higress 01：key-auth 插件本地 Docker 部署与运行机制指南" /&gt;&#10; &lt;blockquote&gt;&#10; &lt;p&gt;目标：不依赖 Kubernetes，只用 Docker 在本地完整复现 &amp;ldquo;McpBridge + Ingress + WasmPlugin(key-auth)&amp;rdquo; 三件套的运行效果（401 / 403 / 200 / 404），并讲清背后的原理。&lt;/p&gt;&#10;&lt;p&gt;本文所有操作已在 macOS + Docker Desktop 上实测通过（2026-09-17）。&lt;/p&gt;&#10;&#10; &lt;/blockquote&gt;&#10;&lt;h2 id="目录"&gt;&lt;a href="#%e7%9b%ae%e5%bd%95" class="header-anchor"&gt;&lt;/a&gt;目录&#10;&lt;/h2&gt;&lt;ul&gt;&#10;&lt;li&gt;&lt;a href="#%e4%b8%80%e8%83%8c%e6%99%af%e8%a6%81%e5%a4%8d%e7%8e%b0%e7%9a%84%e5%8e%9f%e9%85%8d%e7%bd%ae"&gt;一、背景：要复现的原配置&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="#%e4%ba%8c%e6%80%bb%e4%bd%93%e6%96%b9%e6%a1%88%e4%b8%8e%e6%9e%b6%e6%9e%84"&gt;二、总体方案与架构&lt;/a&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;a href="#21-%e4%b8%8e%e5%ae%98%e6%96%b9%e7%8b%ac%e7%ab%8b%e9%83%a8%e7%bd%b2%e6%96%b9%e5%bc%8f%e7%9a%84%e5%85%b3%e7%b3%bb%e5%85%88%e8%af%bb%e8%bf%99%e9%87%8c"&gt;2.1 与官方独立部署方式的关系（先读这里）&lt;/a&gt;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="#%e4%b8%89%e5%8e%9f%e7%90%86%e4%bb%8b%e7%bb%8d"&gt;三、原理介绍&lt;/a&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;a href="#31-wasm-%e6%8f%92%e4%bb%b6%e6%98%af%e6%80%8e%e4%b9%88%e8%b7%91%e8%b5%b7%e6%9d%a5%e7%9a%84"&gt;3.1 WASM 插件是怎么跑起来的&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="#32-proxy-wasm-abi-%e4%b8%8e-0_2_100-%e7%9a%84%e5%9d%91"&gt;3.2 Proxy-Wasm ABI 与 0_2_100 的坑&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="#33-key-auth-%e6%8f%92%e4%bb%b6%e7%9a%84%e8%ae%a4%e8%af%81%e9%80%bb%e8%be%91"&gt;3.3 key-auth 插件的认证逻辑&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="#34-%e8%a7%84%e5%88%99%e5%8c%b9%e9%85%8d_rules_-%e4%b8%8e-_match_route_"&gt;3.4 规则匹配：&lt;em&gt;rules&lt;/em&gt; 与 &lt;em&gt;match_route&lt;/em&gt;&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="#35-%e4%b8%8e%e5%8e%9f-yaml-%e7%9a%84%e5%ad%97%e6%ae%b5%e5%af%b9%e5%ba%94%e5%85%b3%e7%b3%bb"&gt;3.5 与原 YAML 的字段对应关系&lt;/a&gt;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="#%e5%9b%9b%e6%90%ad%e5%bb%ba%e6%ad%a5%e9%aa%a4%e4%bb%8e%e9%9b%b6%e5%bc%80%e5%a7%8b"&gt;四、搭建步骤（从零开始）&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="#%e4%ba%94%e9%aa%8c%e8%af%81%e7%bb%93%e6%9e%9c%e5%ae%9e%e6%b5%8b%e8%be%93%e5%87%ba"&gt;五、验证结果（实测输出）&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="#%e5%85%ad%e8%b8%a9%e5%9d%91%e8%ae%b0%e5%bd%95"&gt;六、踩坑记录&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="#%e4%b8%83%e5%b8%b8%e7%94%a8%e5%91%bd%e4%bb%a4%e9%80%9f%e6%9f%a5"&gt;七、常用命令速查&lt;/a&gt;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;hr&gt;&#10;&lt;h2 id="一背景要复现的原配置"&gt;&lt;a href="#%e4%b8%80%e8%83%8c%e6%99%af%e8%a6%81%e5%a4%8d%e7%8e%b0%e7%9a%84%e5%8e%9f%e9%85%8d%e7%bd%ae" class="header-anchor"&gt;&lt;/a&gt;一、背景：要复现的原配置&#10;&lt;/h2&gt;&lt;p&gt;原始配置是三个 K8s 资源（完整内容见 &lt;code&gt;citic-wasm-plugin/key-auth/keyauth.yaml&lt;/code&gt;）：&lt;/p&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;资源&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;作用&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;关键内容&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;McpBridge mcp-keyauth-httpbin&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;把集群外的 &lt;code&gt;httpbin.org:80&lt;/code&gt; 注册进网关，服务名为 &lt;code&gt;httpbin.dns&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;type: dns&lt;/code&gt;，网关周期性做 DNS 解析得到 IP 池&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;Ingress ingress-keyauth-httpbin&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;路由规则：&lt;code&gt;Host: httpbin.example.com&lt;/code&gt; 的流量全部转发到 &lt;code&gt;httpbin.dns&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;higress.io/destination&lt;/code&gt;、&lt;code&gt;upstream-vhost&lt;/code&gt;、&lt;code&gt;backend-protocol&lt;/code&gt; 三个注解&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;WasmPlugin wasm-keyauth-httpbin&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;在该 Ingress 上启用 key-auth 认证&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;2 个 consumer，只放行 &lt;code&gt;consumer1&lt;/code&gt;；从 &lt;code&gt;x-api-key&lt;/code&gt; / &lt;code&gt;apikey&lt;/code&gt; 头取 Key&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;p&gt;引用关系：&lt;code&gt;Ingress.backend.resource&lt;/code&gt; → McpBridge 名；&lt;code&gt;annotations.higress.io/destination&lt;/code&gt; → &lt;code&gt;name.type&lt;/code&gt;（&lt;code&gt;httpbin.dns&lt;/code&gt;）；&lt;code&gt;WasmPlugin.matchRules[].ingress&lt;/code&gt; → Ingress 名。&lt;/p&gt;&#10;&lt;h2 id="二总体方案与架构"&gt;&lt;a href="#%e4%ba%8c%e6%80%bb%e4%bd%93%e6%96%b9%e6%a1%88%e4%b8%8e%e6%9e%b6%e6%9e%84" class="header-anchor"&gt;&lt;/a&gt;二、总体方案与架构&#10;&lt;/h2&gt;&lt;p&gt;K8s 里这套东西最终也是由 Higress 控制面翻译成 Envoy 配置下发的。所以我们绕过控制面，&lt;strong&gt;直接手写等价的 Envoy 静态配置&lt;/strong&gt;，用 Docker 跑数据面：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt" id="hl-0-1"&gt;&lt;a class="lnlinks" href="#hl-0-1"&gt;1&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-0-2"&gt;&lt;a class="lnlinks" href="#hl-0-2"&gt;2&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-0-3"&gt;&lt;a class="lnlinks" href="#hl-0-3"&gt;3&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-0-4"&gt;&lt;a class="lnlinks" href="#hl-0-4"&gt;4&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-0-5"&gt;&lt;a class="lnlinks" href="#hl-0-5"&gt;5&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-0-6"&gt;&lt;a class="lnlinks" href="#hl-0-6"&gt;6&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-0-7"&gt;&lt;a class="lnlinks" href="#hl-0-7"&gt;7&lt;/a&gt;&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;┌─────────┐ curl -H &amp;#34;Host: httpbin.example.com&amp;#34; ┌──────────────────────────────┐ ┌──────────────────┐&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ 终端 │ ──────────── localhost:10000 ──────────▶ │ envoy 容器 (higress/gateway) │ ───▶ │ httpbin 容器 │&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ (curl) │ ◀────────────── 401/403/200 ──────────── │ ┌────────────────────────┐ │ │ go-httpbin :8080 │&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;└─────────┘ │ │ key-auth wasm 插件 │ │ │ （代替 httpbin.org）│&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ │ (本地 Go 源码编译) │ │ └──────────────────┘&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ └────────────────────────┘ │&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; └──────────────────────────────┘&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;相比原方案的两处替换：&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;strong&gt;上游替换&lt;/strong&gt;：&lt;code&gt;httpbin.org&lt;/code&gt;（公网）→ 本地 &lt;code&gt;mccutchen/go-httpbin&lt;/code&gt; 容器。行为等价（都是请求回显服务），且不依赖外网。&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;控制面替换&lt;/strong&gt;：McpBridge/Ingress/WasmPlugin 三个 CRD → 一份 &lt;code&gt;envoy.yaml&lt;/code&gt;。三者的语义被完整保留（域名匹配、路由名、插件配置、allow 白名单）。&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;所有文件在 &lt;code&gt;key-auth-docker-demo/&lt;/code&gt; 目录：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt" id="hl-1-1"&gt;&lt;a class="lnlinks" href="#hl-1-1"&gt;1&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-1-2"&gt;&lt;a class="lnlinks" href="#hl-1-2"&gt;2&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-1-3"&gt;&lt;a class="lnlinks" href="#hl-1-3"&gt;3&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-1-4"&gt;&lt;a class="lnlinks" href="#hl-1-4"&gt;4&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-1-5"&gt;&lt;a class="lnlinks" href="#hl-1-5"&gt;5&lt;/a&gt;&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;key-auth-docker-demo/&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;├── docker-compose.yaml # 两个容器：envoy + httpbin&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;├── envoy.yaml # 等价于三个 CRD 的 Envoy 静态配置&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;├── plugin.wasm # 用 golang:1.24 容器从本仓库 Go 源码编译&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;└── test.sh # 5 个场景的验证脚本&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;h3 id="21-与官方独立部署方式的关系先读这里"&gt;&lt;a href="#21-%e4%b8%8e%e5%ae%98%e6%96%b9%e7%8b%ac%e7%ab%8b%e9%83%a8%e7%bd%b2%e6%96%b9%e5%bc%8f%e7%9a%84%e5%85%b3%e7%b3%bb%e5%85%88%e8%af%bb%e8%bf%99%e9%87%8c" class="header-anchor"&gt;&lt;/a&gt;2.1 与官方独立部署方式的关系（先读这里）&#10;&lt;/h3&gt;&lt;p&gt;Higress 官方文档提供了三种&amp;quot;脱离/本地化 K8s&amp;quot;的部署方式，它们和本文方案&lt;strong&gt;不是一回事&lt;/strong&gt;，定位不同：&lt;/p&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;方式&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;组件规模&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;配置方式&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;适用场景&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;官方文档&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;All-in-One 单容器&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;1 个容器（gateway+console 打包）&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;控制台 UI / 本地 &lt;code&gt;/data&lt;/code&gt; 卷&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;快速体验完整网关功能&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;a href="https://higress.cn/docs/latest/ops/deploy-by-docker/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;deploy-by-docker&lt;span class="external-icon"&gt;↗&lt;/span&gt;&lt;/a&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Docker Compose 独立部署&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;apiserver + controller + pilot + gateway + console（可选内置 nacos、Prometheus/Loki/Grafana 监控套件），约 12 个容器&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;控制台 UI / 模拟 K8s API / 本地配置文件&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;接近真实架构的本地测试&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;a href="https://higress.cn/docs/latest/ops/deploy-by-docker-compose/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;deploy-by-docker-compose&lt;span class="external-icon"&gt;↗&lt;/span&gt;&lt;/a&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;本地 K8s（kind）+ Helm&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;完整云原生模式&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;kubectl apply&lt;/code&gt; CRD（本文开头那份 YAML 可直接用）&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;与生产完全一致的验证&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;a href="https://higress.cn/docs/latest/user/quickstart/" target="_blank" rel="noopener noreferrer" class="external-link"&gt;quickstart&lt;span class="external-icon"&gt;↗&lt;/span&gt;&lt;/a&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;strong&gt;本文方案：裸数据面&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;2 个容器（envoy + httpbin）&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;手写 &lt;code&gt;envoy.yaml&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;只关心&lt;strong&gt;插件本身的行为和原理&lt;/strong&gt;，最轻最快&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;本文&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;p&gt;本文方案可以理解成：把官方架构中 pilot 最终下发给 gateway 的那份 Envoy 配置&amp;quot;手写&amp;quot;了出来，&lt;strong&gt;只跑数据面，不跑控制面&lt;/strong&gt;。对&amp;quot;验证 key-auth 插件的认证行为（401/403/200）&amp;ldquo;这个目的来说，2 个镜像、一条 &lt;code&gt;docker compose up -d&lt;/code&gt; 就够了。&lt;/p&gt;&#10;&lt;p&gt;官方两种方式的核心命令（引自官方文档，供需要完整控制台时使用）：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt" id="hl-2-1"&gt;&lt;a class="lnlinks" href="#hl-2-1"&gt; 1&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-2-2"&gt;&lt;a class="lnlinks" href="#hl-2-2"&gt; 2&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-2-3"&gt;&lt;a class="lnlinks" href="#hl-2-3"&gt; 3&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-2-4"&gt;&lt;a class="lnlinks" href="#hl-2-4"&gt; 4&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-2-5"&gt;&lt;a class="lnlinks" href="#hl-2-5"&gt; 5&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-2-6"&gt;&lt;a class="lnlinks" href="#hl-2-6"&gt; 6&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-2-7"&gt;&lt;a class="lnlinks" href="#hl-2-7"&gt; 7&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-2-8"&gt;&lt;a class="lnlinks" href="#hl-2-8"&gt; 8&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-2-9"&gt;&lt;a class="lnlinks" href="#hl-2-9"&gt; 9&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-2-10"&gt;&lt;a class="lnlinks" href="#hl-2-10"&gt;10&lt;/a&gt;&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 方式一：All-in-One 单容器（控制台 8001，网关 8080/8443，配置存到当前目录）&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;mkdir higress&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;cd&lt;/span&gt; higress&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker run -d --rm --name higress-ai -v &lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PWD&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;:/data &lt;span class="se"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -p 8001:8001 -p 8080:8080 -p 8443:8443 &lt;span class="se"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; higress-registry.cn-hangzhou.cr.aliyuncs.com/higress/all-in-one:latest&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 方式二：Docker Compose 多组件独立部署&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -fsSL https://higress.io/standalone/get-higress.sh &lt;span class="p"&gt;|&lt;/span&gt; bash -s -- &lt;span class="o"&gt;[&lt;/span&gt;安装目录&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;选项&lt;span class="o"&gt;]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 常用选项：-a 装完自动启动；--use-builtin-nacos 用内置配置中心；&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# -c file://&amp;lt;绝对路径&amp;gt; 用本地文件存配置；--console-port=8001 改控制台端口（默认 8080）&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;实测备注&lt;/strong&gt;（2026-09-17 本机实际尝试过 compose 独立部署后的经验，供参考）：&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;strong&gt;重&lt;/strong&gt;：约 12 个镜像（apiserver、controller、pilot、gateway、console、plugin-server、nacos、prometheus、loki、grafana 等），首次安装拉取超过 30 分钟；日常验证插件没必要。&lt;/li&gt;&#10;&lt;li&gt;&lt;code&gt;-c file:///opt/higress/conf&lt;/code&gt; 默认会把配置写到宿主机 &lt;code&gt;/opt/higress&lt;/code&gt;（macOS 上直接 &lt;code&gt;Permission denied&lt;/code&gt;），要改成工作区内的绝对路径。&lt;/li&gt;&#10;&lt;li&gt;安装脚本内部用 &lt;code&gt;docker compose -p higress&lt;/code&gt; 管理容器；如果自己在 &lt;code&gt;compose/&lt;/code&gt; 目录里手动执行 &lt;code&gt;docker compose&lt;/code&gt; 而不带 &lt;code&gt;-p higress&lt;/code&gt;，会另建一套 &lt;code&gt;compose-*&lt;/code&gt; 前缀的重复容器并抢 80 端口报错。&lt;/li&gt;&#10;&lt;li&gt;一个利好：compose 部署内置 &lt;code&gt;plugin-server&lt;/code&gt; 容器，自带 key-auth 2.0.0 等官方插件，&lt;code&gt;WasmPlugin.url&lt;/code&gt; 直接写 &lt;code&gt;http://plugin-server:8080/plugins/key-auth/2.0.0/plugin.wasm&lt;/code&gt; 即可，&lt;strong&gt;不需要自己编译 wasm&lt;/strong&gt;——但那样跑的就不是本仓库的代码了。&lt;/li&gt;&#10;&lt;li&gt;卸载干净的方法：安装目录下 &lt;code&gt;bash bin/reset.sh&lt;/code&gt;（停容器、删数据卷），再删安装目录和镜像。&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;&lt;strong&gt;结论&lt;/strong&gt;：想体验控制台和完整控制面 → 用上面官方方式；想验证/调试&lt;strong&gt;本仓库的插件代码&lt;/strong&gt;、搞清楚数据面到底发生了什么 → 用本文方案。&lt;/p&gt;&#10;&lt;h2 id="三原理介绍"&gt;&lt;a href="#%e4%b8%89%e5%8e%9f%e7%90%86%e4%bb%8b%e7%bb%8d" class="header-anchor"&gt;&lt;/a&gt;三、原理介绍&#10;&lt;/h2&gt;&lt;h3 id="31-wasm-插件是怎么跑起来的"&gt;&lt;a href="#31-wasm-%e6%8f%92%e4%bb%b6%e6%98%af%e6%80%8e%e4%b9%88%e8%b7%91%e8%b5%b7%e6%9d%a5%e7%9a%84" class="header-anchor"&gt;&lt;/a&gt;3.1 WASM 插件是怎么跑起来的&#10;&lt;/h3&gt;&lt;ol&gt;&#10;&lt;li&gt;Higress 的数据面是 Envoy 的一个 fork。Envoy 内置 &lt;code&gt;envoy.filters.http.wasm&lt;/code&gt; 过滤器，可以把一个 &lt;code&gt;.wasm&lt;/code&gt; 文件加载进 V8 运行时，作为 HTTP 过滤器链的一环执行。&lt;/li&gt;&#10;&lt;li&gt;插件本身是用 Go 写的（&lt;code&gt;citic-wasm-plugin/key-auth/main.go&lt;/code&gt;），基于 &lt;code&gt;higress-group/proxy-wasm-go-sdk&lt;/code&gt;，通过 &lt;code&gt;GOOS=wasip1 GOARCH=wasm go build -buildmode=c-shared&lt;/code&gt; 编译成 wasm。&lt;/li&gt;&#10;&lt;li&gt;插件通过 &lt;code&gt;wrapper.SetCtx(&amp;quot;key-auth&amp;quot;, ...)&lt;/code&gt; 注册三类回调：&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;code&gt;ParseOverrideConfigBy(parseGlobalConfig, parseOverrideRuleConfig)&lt;/code&gt; — 插件启动/配置变更时解析配置&lt;/li&gt;&#10;&lt;li&gt;&lt;code&gt;ProcessRequestHeadersBy(onHttpRequestHeaders)&lt;/code&gt; — 每个请求到达时执行认证判断&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;请求流程：Envoy 收到请求 → 路由匹配（host + path）→ 执行 wasm 过滤器（key-auth 认证）→ 认证通过则转发给上游 cluster，并把 &lt;code&gt;X-Mse-Consumer: &amp;lt;consumer名&amp;gt;&lt;/code&gt; 头注入给上游；不通过则由插件直接返回本地响应（401/403），请求根本不会到达上游。&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p&gt;插件里发回本地响应用的是 &lt;code&gt;proxywasm.SendHttpResponseWithDetail&lt;/code&gt;（&lt;code&gt;main.go:329-345&lt;/code&gt;），同时带上 &lt;code&gt;WWW-Authenticate: Key realm=MSE Gateway&lt;/code&gt; 响应头——这就是实测输出里 &lt;code&gt;www-authenticate: Key realm=MSE Gateway&lt;/code&gt; 的来源。&lt;/p&gt;&#10;&lt;h3 id="32-proxy-wasm-abi-与-0_2_100-的坑"&gt;&lt;a href="#32-proxy-wasm-abi-%e4%b8%8e-0_2_100-%e7%9a%84%e5%9d%91" class="header-anchor"&gt;&lt;/a&gt;3.2 Proxy-Wasm ABI 与 0_2_100 的坑&#10;&lt;/h3&gt;&lt;p&gt;Envoy 与 wasm 插件之间通过 &lt;strong&gt;Proxy-Wasm ABI&lt;/strong&gt;（一组以 &lt;code&gt;proxy_&lt;/code&gt; 开头的导入/导出函数）通信。Envoy 加载 wasm 时会检查模块导出的 ABI 版本号函数：&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;官方 Envoy 认 &lt;code&gt;proxy_abi_version_0_2_1&lt;/code&gt;&lt;/li&gt;&#10;&lt;li&gt;用官方 C++ SDK 编译的插件（如官方镜像 &lt;code&gt;plugins/key-auth:1.0.0&lt;/code&gt; 里的 wasm）导出的就是 &lt;code&gt;proxy_abi_version_0_2_1&lt;/code&gt;&lt;/li&gt;&#10;&lt;li&gt;但 &lt;strong&gt;Higress 的 Go SDK fork 导出的是 &lt;code&gt;proxy_abi_version_0_2_100&lt;/code&gt;&lt;/strong&gt;（Higress 私有扩展 ABI，增加了 redis 调用等能力）&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;验证方法（无需反编译器，strings 即可）：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt" id="hl-3-1"&gt;&lt;a class="lnlinks" href="#hl-3-1"&gt;1&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-3-2"&gt;&lt;a class="lnlinks" href="#hl-3-2"&gt;2&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-3-3"&gt;&lt;a class="lnlinks" href="#hl-3-3"&gt;3&lt;/a&gt;&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;strings plugin.wasm &lt;span class="p"&gt;|&lt;/span&gt; grep proxy_abi&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Go 编译（本仓库源码）: proxy_abi_version_0_2_100&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 官方 C++ 编译镜像: proxy_abi_version_0_2_1&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;结论&lt;/strong&gt;：Go 源码编译的 key-auth 插件&lt;strong&gt;只能跑在 Higress fork 的 Envoy 上&lt;/strong&gt;。如果挂载到官方 &lt;code&gt;envoyproxy/envoy&lt;/code&gt;，启动即失败：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt" id="hl-4-1"&gt;&lt;a class="lnlinks" href="#hl-4-1"&gt;1&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-4-2"&gt;&lt;a class="lnlinks" href="#hl-4-2"&gt;2&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-4-3"&gt;&lt;a class="lnlinks" href="#hl-4-3"&gt;3&lt;/a&gt;&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;wasm&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="n"&gt;Wasm&lt;/span&gt; &lt;span class="n"&gt;VM&lt;/span&gt; &lt;span class="n"&gt;failed&lt;/span&gt; &lt;span class="n"&gt;Missing&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;unknown&lt;/span&gt; &lt;span class="n"&gt;Proxy&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Wasm&lt;/span&gt; &lt;span class="n"&gt;ABI&lt;/span&gt; &lt;span class="n"&gt;version&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;critical&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;wasm&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="n"&gt;Plugin&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;auth&lt;/span&gt; &lt;span class="n"&gt;failed&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="nb"&gt;load&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;critical&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt; &lt;span class="n"&gt;initializing&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;/etc/envoy/envoy.yaml&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Unable&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;create&lt;/span&gt; &lt;span class="n"&gt;Wasm&lt;/span&gt; &lt;span class="n"&gt;plugin&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;auth&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;所以本方案使用 &lt;code&gt;higress-registry.cn-hangzhou.cr.aliyuncs.com/higress/gateway:v2.2.4&lt;/code&gt;（内含 Envoy 1.36.4 的 Higress fork），通过覆盖 entrypoint 直接裸跑 envoy：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt" id="hl-5-1"&gt;&lt;a class="lnlinks" href="#hl-5-1"&gt;1&lt;/a&gt;&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;entrypoint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;/usr/local/bin/envoy&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;-c&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;/etc/envoy/envoy.yaml&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;--concurrency&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;1&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;h3 id="33-key-auth-插件的认证逻辑"&gt;&lt;a href="#33-key-auth-%e6%8f%92%e4%bb%b6%e7%9a%84%e8%ae%a4%e8%af%81%e9%80%bb%e8%be%91" class="header-anchor"&gt;&lt;/a&gt;3.3 key-auth 插件的认证逻辑&#10;&lt;/h3&gt;&lt;p&gt;核心逻辑在 &lt;code&gt;main.go&lt;/code&gt; 的 &lt;code&gt;onHttpRequestHeaders&lt;/code&gt;（&lt;code&gt;main.go:239&lt;/code&gt;）。插件行为由两个变量决定：&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;code&gt;global_auth&lt;/code&gt;：全局配置里的开关（true / false / 未设置，三种状态）&lt;/li&gt;&#10;&lt;li&gt;当前路由是否配置了 &lt;code&gt;allow&lt;/code&gt; 列表（即 matchRules 是否命中当前路由）&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;决策真值表：&lt;/p&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;global_auth&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;当前路由有 allow&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;行为&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;true&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;否&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;全局限流：所有 consumer 的 Key 都可用&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;true&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;是&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;只有 allow 列表里的 consumer 可用&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;false&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;否&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;strong&gt;直接放行，不认证&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;false&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;是&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;只有 allow 列表里的 consumer 可用&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;未设置&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;全部路由都没配&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;退化为全局认证&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;未设置&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;至少一个路由配了&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;等价于 false：没配的路由放行，配了的路由按 allow 校验&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;p&gt;本次复现的配置是 &lt;code&gt;global_auth: false&lt;/code&gt; + 路由配 &lt;code&gt;allow: [consumer1]&lt;/code&gt;，即上表第 4 行。&lt;/p&gt;&#10;&lt;p&gt;取 Key 与判定顺序（&lt;code&gt;main.go:259-326&lt;/code&gt;）：&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;按 &lt;code&gt;keys&lt;/code&gt; 列表从 Header（&lt;code&gt;in_header: true&lt;/code&gt;）或 URL query（&lt;code&gt;in_query: true&lt;/code&gt;）取 token&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;取到多个 token → 401&lt;/strong&gt;（&lt;code&gt;deniedMultiKeyAuthData&lt;/code&gt;）&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;一个都没取到 → 401&lt;/strong&gt;（&lt;code&gt;deniedNoKeyAuthData&lt;/code&gt;，body: &lt;code&gt;No Key Authentication information found&lt;/code&gt;）&lt;/li&gt;&#10;&lt;li&gt;token 在 consumers 里查不到 → &lt;strong&gt;403&lt;/strong&gt;（&lt;code&gt;deniedUnauthorizedConsumer&lt;/code&gt;，body: &lt;code&gt;Unauthorized consumer&lt;/code&gt;）&lt;/li&gt;&#10;&lt;li&gt;查到 consumer 后先注入 &lt;code&gt;X-Mse-Consumer: &amp;lt;name&amp;gt;&lt;/code&gt; 头，再检查是否在 &lt;code&gt;allow&lt;/code&gt; 列表：&#10;&lt;ul&gt;&#10;&lt;li&gt;不在 → &lt;strong&gt;403&lt;/strong&gt;（&lt;code&gt;deniedUnauthorizedConsumer&lt;/code&gt;）&lt;/li&gt;&#10;&lt;li&gt;在 → 放行，请求转发上游&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&#10; &lt;blockquote&gt;&#10; &lt;p&gt;⚠️ 注意：&lt;strong&gt;假 Key 和&amp;quot;不在白名单的有效 Key&amp;quot;都是 403&lt;/strong&gt;，只有&amp;quot;完全不带 Key&amp;quot;或&amp;quot;带多个 Key&amp;quot;才是 401。原 YAML 讲解文档里说假 Key 返回 401 是不准确的，以源码和实测为准。&lt;/p&gt;&#10;&#10; &lt;/blockquote&gt;&#10;&lt;h3 id="34-规则匹配rules-与-match_"&gt;&lt;a href="#34-%e8%a7%84%e5%88%99%e5%8c%b9%e9%85%8drules-%e4%b8%8e-match_" class="header-anchor"&gt;&lt;/a&gt;3.4 规则匹配：&lt;em&gt;rules&lt;/em&gt; 与 &lt;em&gt;match_route&lt;/em&gt;&#10;&lt;/h3&gt;&lt;p&gt;K8s 里 &lt;code&gt;WasmPlugin.matchRules[].ingress: [ingress-keyauth-httpbin]&lt;/code&gt; 经控制面翻译后，落到 wasm 插件配置里是：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt" id="hl-6-1"&gt;&lt;a class="lnlinks" href="#hl-6-1"&gt;1&lt;/a&gt;&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;&amp;#34;_rules_&amp;#34;&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="nt"&gt;&amp;#34;_match_route_&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&amp;lt;路由名&amp;gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;allow&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;consumer1&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]}]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;wasm-go SDK 的 wrapper 在每次请求时取 Envoy 的 &lt;code&gt;route_name&lt;/code&gt; 属性，和 &lt;code&gt;_match_route_&lt;/code&gt; 列表比对：命中则用该条规则的配置（覆盖全局配置中的 &lt;code&gt;allow&lt;/code&gt;），不命中则用全局配置。&lt;/p&gt;&#10;&lt;p&gt;因此在裸 Envoy 配置里，我们只要给路由起个名字（&lt;code&gt;name: keyauth-route&lt;/code&gt;），并在插件配置里写 &lt;code&gt;_match_route_: [&amp;quot;keyauth-route&amp;quot;]&lt;/code&gt;，就等价复现了 matchRules 的语义。&lt;/p&gt;&#10;&lt;h3 id="35-与原-yaml-的字段对应关系"&gt;&lt;a href="#35-%e4%b8%8e%e5%8e%9f-yaml-%e7%9a%84%e5%ad%97%e6%ae%b5%e5%af%b9%e5%ba%94%e5%85%b3%e7%b3%bb" class="header-anchor"&gt;&lt;/a&gt;3.5 与原 YAML 的字段对应关系&#10;&lt;/h3&gt;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;原 YAML 字段&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;envoy.yaml 中的等价物&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;Ingress rules[0].host: httpbin.example.com&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;virtual_hosts[].domains: [&amp;quot;httpbin.example.com&amp;quot;]&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;path: /&lt;/code&gt; + &lt;code&gt;pathType: Prefix&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;routes[].match.prefix: /&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;McpBridge&lt;/code&gt;（httpbin.org:80, type: dns）&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;clusters[].name: httpbin&lt;/code&gt; + &lt;code&gt;STRICT_DNS&lt;/code&gt; 指向本地 httpbin 容器&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;higress.io/upstream-vhost: httpbin.org&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;本地回显服务不校验 Host，省略；真实场景对应 &lt;code&gt;host_rewrite_literal&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;matchRules[].ingress&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;路由命名 &lt;code&gt;keyauth-route&lt;/code&gt; + 插件配置 &lt;code&gt;_match_route_&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;defaultConfig&lt;/code&gt;（consumers/keys/in_header/global_auth）&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;插件 JSON 配置的同名字段&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;matchRules[].config.allow: [consumer1]&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;_rules_[0].allow: [&amp;quot;consumer1&amp;quot;]&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;p&gt;&lt;code&gt;envoy.yaml&lt;/code&gt; 中插件配置的完整 JSON（即 defaultConfig + matchRules 合并后的结果）：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt" id="hl-7-1"&gt;&lt;a class="lnlinks" href="#hl-7-1"&gt; 1&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-7-2"&gt;&lt;a class="lnlinks" href="#hl-7-2"&gt; 2&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-7-3"&gt;&lt;a class="lnlinks" href="#hl-7-3"&gt; 3&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-7-4"&gt;&lt;a class="lnlinks" href="#hl-7-4"&gt; 4&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-7-5"&gt;&lt;a class="lnlinks" href="#hl-7-5"&gt; 5&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-7-6"&gt;&lt;a class="lnlinks" href="#hl-7-6"&gt; 6&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-7-7"&gt;&lt;a class="lnlinks" href="#hl-7-7"&gt; 7&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-7-8"&gt;&lt;a class="lnlinks" href="#hl-7-8"&gt; 8&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-7-9"&gt;&lt;a class="lnlinks" href="#hl-7-9"&gt; 9&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-7-10"&gt;&lt;a class="lnlinks" href="#hl-7-10"&gt;10&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-7-11"&gt;&lt;a class="lnlinks" href="#hl-7-11"&gt;11&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-7-12"&gt;&lt;a class="lnlinks" href="#hl-7-12"&gt;12&lt;/a&gt;&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;consumers&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nt"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;consumer1&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;credential&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;2bda943c-ba2b-11ec-ba07-00163e1250b5&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nt"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;consumer2&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;credential&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;c8c8e9ca-558e-4a2d-bb62-e700dcc40e35&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;],&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;keys&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;x-api-key&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;apikey&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;in_header&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;global_auth&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;_rules_&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nt"&gt;&amp;#34;_match_route_&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;keyauth-route&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;allow&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;consumer1&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;h2 id="四搭建步骤从零开始"&gt;&lt;a href="#%e5%9b%9b%e6%90%ad%e5%bb%ba%e6%ad%a5%e9%aa%a4%e4%bb%8e%e9%9b%b6%e5%bc%80%e5%a7%8b" class="header-anchor"&gt;&lt;/a&gt;四、搭建步骤（从零开始）&#10;&lt;/h2&gt;&lt;h3 id="41-准备镜像"&gt;&lt;a href="#41-%e5%87%86%e5%a4%87%e9%95%9c%e5%83%8f" class="header-anchor"&gt;&lt;/a&gt;4.1 准备镜像&#10;&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt" id="hl-8-1"&gt;&lt;a class="lnlinks" href="#hl-8-1"&gt;1&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-8-2"&gt;&lt;a class="lnlinks" href="#hl-8-2"&gt;2&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-8-3"&gt;&lt;a class="lnlinks" href="#hl-8-3"&gt;3&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-8-4"&gt;&lt;a class="lnlinks" href="#hl-8-4"&gt;4&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-8-5"&gt;&lt;a class="lnlinks" href="#hl-8-5"&gt;5&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-8-6"&gt;&lt;a class="lnlinks" href="#hl-8-6"&gt;6&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-8-7"&gt;&lt;a class="lnlinks" href="#hl-8-7"&gt;7&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-8-8"&gt;&lt;a class="lnlinks" href="#hl-8-8"&gt;8&lt;/a&gt;&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Higress gateway（含支持 Go 插件 ABI 的 Envoy fork）&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker pull higress-registry.cn-hangzhou.cr.aliyuncs.com/higress/gateway:v2.2.4&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 本地回显服务&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker pull mccutchen/go-httpbin:latest&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 编译用的 golang 镜像&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker pull golang:1.24&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&#10; &lt;blockquote&gt;&#10; &lt;p&gt;docker.io 不通时可走镜像加速器，例如 DaoCloud：&#10;&lt;code&gt;docker pull docker.m.daocloud.io/library/golang:1.24 &amp;amp;&amp;amp; docker tag docker.m.daocloud.io/library/golang:1.24 golang:1.24&lt;/code&gt;。&#10;注意 &lt;code&gt;mccutchen/go-httpbin&lt;/code&gt; 不在 DaoCloud 白名单内，需直连 docker.io 或开代理；阿里云 registry（&lt;code&gt;higress-registry.cn-hangzhou.cr.aliyuncs.com&lt;/code&gt;）国内直连无压力。&lt;/p&gt;&#10;&#10; &lt;/blockquote&gt;&#10;&lt;h3 id="42-编译-pluginwasm"&gt;&lt;a href="#42-%e7%bc%96%e8%af%91-pluginwasm" class="header-anchor"&gt;&lt;/a&gt;4.2 编译 plugin.wasm&#10;&lt;/h3&gt;&lt;p&gt;用 Docker 编译本仓库的 Go 源码，不污染本地 Go 环境：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt" id="hl-9-1"&gt;&lt;a class="lnlinks" href="#hl-9-1"&gt;1&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-9-2"&gt;&lt;a class="lnlinks" href="#hl-9-2"&gt;2&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-9-3"&gt;&lt;a class="lnlinks" href="#hl-9-3"&gt;3&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-9-4"&gt;&lt;a class="lnlinks" href="#hl-9-4"&gt;4&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-9-5"&gt;&lt;a class="lnlinks" href="#hl-9-5"&gt;5&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-9-6"&gt;&lt;a class="lnlinks" href="#hl-9-6"&gt;6&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-9-7"&gt;&lt;a class="lnlinks" href="#hl-9-7"&gt;7&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-9-8"&gt;&lt;a class="lnlinks" href="#hl-9-8"&gt;8&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-9-9"&gt;&lt;a class="lnlinks" href="#hl-9-9"&gt;9&lt;/a&gt;&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; citic-wasm-plugin/key-auth&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker run --rm &lt;span class="se"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -v &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$PWD&lt;/span&gt;&lt;span class="s2"&gt;:/src&amp;#34;&lt;/span&gt; -w /src &lt;span class="se"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -v gomodcache:/go/pkg/mod -v gocache:/root/.cache/go-build &lt;span class="se"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -e &lt;span class="nv"&gt;GOOS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;wasip1 -e &lt;span class="nv"&gt;GOARCH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;wasm -e &lt;span class="nv"&gt;GOPROXY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https://goproxy.cn,direct &lt;span class="se"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; golang:1.24 go build -buildmode&lt;span class="o"&gt;=&lt;/span&gt;c-shared -o plugin.wasm .&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 拷贝到部署目录（compose 里挂载的是 key-auth-docker-demo/plugin.wasm）&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;cp plugin.wasm ../../key-auth-docker-demo/&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;产物约 5.4MB。&lt;code&gt;gomodcache&lt;/code&gt;/&lt;code&gt;gocache&lt;/code&gt; 两个 named volume 用于缓存依赖，二次编译会快很多。&lt;/p&gt;&#10;&#10; &lt;blockquote&gt;&#10; &lt;p&gt;备选：也可以直接从官方 OCI 镜像提取预编译 wasm（注意是 C++ 版本，ABI 为 0_2_1，可跑在官方 Envoy 上，但拒绝响应的文案与 Go 版不同）：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt" id="hl-10-1"&gt;&lt;a class="lnlinks" href="#hl-10-1"&gt;1&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-10-2"&gt;&lt;a class="lnlinks" href="#hl-10-2"&gt;2&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-10-3"&gt;&lt;a class="lnlinks" href="#hl-10-3"&gt;3&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-10-4"&gt;&lt;a class="lnlinks" href="#hl-10-4"&gt;4&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-10-5"&gt;&lt;a class="lnlinks" href="#hl-10-5"&gt;5&lt;/a&gt;&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;curl -s &lt;span class="s2"&gt;&amp;#34;https://dockerauth.cn-hangzhou.aliyuncs.com/auth?service=registry.aliyuncs.com:cn-hangzhou:china:cri-r0xfyoxudmtseqq7&amp;amp;scope=repository:plugins/key-auth:pull&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;|&lt;/span&gt; python3 -c &lt;span class="s2"&gt;&amp;#34;import sys,json;print(json.load(sys.stdin)[&amp;#39;token&amp;#39;])&amp;#34;&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 先 GET /v2/plugins/key-auth/manifests/1.0.0 找到 wasm 层的 digest，再：&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -sL -H &lt;span class="s2"&gt;&amp;#34;Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$TOKEN&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; -o plugin.wasm &lt;span class="se"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;https://higress-registry.cn-hangzhou.cr.aliyuncs.com/v2/plugins/key-auth/blobs/&amp;lt;digest&amp;gt;&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&#10; &lt;/blockquote&gt;&#10;&lt;h3 id="43-编写配置"&gt;&lt;a href="#43-%e7%bc%96%e5%86%99%e9%85%8d%e7%bd%ae" class="header-anchor"&gt;&lt;/a&gt;4.3 编写配置&#10;&lt;/h3&gt;&lt;p&gt;&lt;code&gt;envoy.yaml&lt;/code&gt; 和 &lt;code&gt;docker-compose.yaml&lt;/code&gt; 已就绪（见 &lt;code&gt;key-auth-docker-demo/&lt;/code&gt;），关键点：&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;监听 &lt;code&gt;0.0.0.0:10000&lt;/code&gt;，宿主机映射 &lt;code&gt;10000:10000&lt;/code&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;code&gt;admin&lt;/code&gt; 开在 9901 端口（可选，用于看配置和统计）&lt;/li&gt;&#10;&lt;li&gt;wasm 过滤器配置中 &lt;code&gt;runtime: envoy.wasm.runtime.v8&lt;/code&gt;，&lt;code&gt;code.local.filename: /etc/envoy/plugin.wasm&lt;/code&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;code&gt;plugin.wasm&lt;/code&gt; 以只读方式挂载进容器&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h3 id="44-启动与验证"&gt;&lt;a href="#44-%e5%90%af%e5%8a%a8%e4%b8%8e%e9%aa%8c%e8%af%81" class="header-anchor"&gt;&lt;/a&gt;4.4 启动与验证&#10;&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt" id="hl-11-1"&gt;&lt;a class="lnlinks" href="#hl-11-1"&gt;1&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-11-2"&gt;&lt;a class="lnlinks" href="#hl-11-2"&gt;2&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-11-3"&gt;&lt;a class="lnlinks" href="#hl-11-3"&gt;3&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-11-4"&gt;&lt;a class="lnlinks" href="#hl-11-4"&gt;4&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-11-5"&gt;&lt;a class="lnlinks" href="#hl-11-5"&gt;5&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-11-6"&gt;&lt;a class="lnlinks" href="#hl-11-6"&gt;6&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-11-7"&gt;&lt;a class="lnlinks" href="#hl-11-7"&gt;7&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-11-8"&gt;&lt;a class="lnlinks" href="#hl-11-8"&gt;8&lt;/a&gt;&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; key-auth-docker-demo&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker compose up -d&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 确认插件加载成功&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker compose logs envoy &lt;span class="p"&gt;|&lt;/span&gt; grep &lt;span class="s2"&gt;&amp;#34;plugin start successfully&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 跑全部验证场景&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;bash test.sh&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;h2 id="五验证结果实测输出"&gt;&lt;a href="#%e4%ba%94%e9%aa%8c%e8%af%81%e7%bb%93%e6%9e%9c%e5%ae%9e%e6%b5%8b%e8%be%93%e5%87%ba" class="header-anchor"&gt;&lt;/a&gt;五、验证结果（实测输出）&#10;&lt;/h2&gt;&lt;h3 id="-不带-key--401"&gt;&lt;a href="#-%e4%b8%8d%e5%b8%a6-key--401" class="header-anchor"&gt;&lt;/a&gt;① 不带 Key → 401&#10;&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt" id="hl-12-1"&gt;&lt;a class="lnlinks" href="#hl-12-1"&gt;1&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-12-2"&gt;&lt;a class="lnlinks" href="#hl-12-2"&gt;2&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-12-3"&gt;&lt;a class="lnlinks" href="#hl-12-3"&gt;3&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-12-4"&gt;&lt;a class="lnlinks" href="#hl-12-4"&gt;4&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-12-5"&gt;&lt;a class="lnlinks" href="#hl-12-5"&gt;5&lt;/a&gt;&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;HTTP/1.1 401 Unauthorized&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;www-authenticate: Key realm=MSE Gateway&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;server: envoy&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Request denied by Key Auth check. No Key Authentication information found.&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;h3 id="-consumer1-的-key在白名单-200"&gt;&lt;a href="#-consumer1-%e7%9a%84-key%e5%9c%a8%e7%99%bd%e5%90%8d%e5%8d%95-200" class="header-anchor"&gt;&lt;/a&gt;② consumer1 的 Key（在白名单）→ 200&#10;&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt" id="hl-13-1"&gt;&lt;a class="lnlinks" href="#hl-13-1"&gt;1&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-13-2"&gt;&lt;a class="lnlinks" href="#hl-13-2"&gt;2&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-13-3"&gt;&lt;a class="lnlinks" href="#hl-13-3"&gt;3&lt;/a&gt;&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -s -i -H &lt;span class="s2"&gt;&amp;#34;Host: httpbin.example.com&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -H &lt;span class="s2"&gt;&amp;#34;x-api-key: 2bda943c-ba2b-11ec-ba07-00163e1250b5&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; http://localhost:10000/get&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt" id="hl-14-1"&gt;&lt;a class="lnlinks" href="#hl-14-1"&gt; 1&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-14-2"&gt;&lt;a class="lnlinks" href="#hl-14-2"&gt; 2&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-14-3"&gt;&lt;a class="lnlinks" href="#hl-14-3"&gt; 3&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-14-4"&gt;&lt;a class="lnlinks" href="#hl-14-4"&gt; 4&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-14-5"&gt;&lt;a class="lnlinks" href="#hl-14-5"&gt; 5&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-14-6"&gt;&lt;a class="lnlinks" href="#hl-14-6"&gt; 6&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-14-7"&gt;&lt;a class="lnlinks" href="#hl-14-7"&gt; 7&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-14-8"&gt;&lt;a class="lnlinks" href="#hl-14-8"&gt; 8&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-14-9"&gt;&lt;a class="lnlinks" href="#hl-14-9"&gt; 9&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-14-10"&gt;&lt;a class="lnlinks" href="#hl-14-10"&gt;10&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-14-11"&gt;&lt;a class="lnlinks" href="#hl-14-11"&gt;11&lt;/a&gt;&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;HTTP/1.1 200 OK&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;server: envoy&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;x-envoy-upstream-service-time: 16&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;headers&amp;#34;: {&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;Host&amp;#34;: [&amp;#34;httpbin.example.com&amp;#34;],&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;X-Mse-Consumer&amp;#34;: [&amp;#34;consumer1&amp;#34;], ← 插件注入，证明上游收到了认证身份&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ...&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; }&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;}&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;h3 id="-consumer2-的-key有效但不在-allow-403"&gt;&lt;a href="#-consumer2-%e7%9a%84-key%e6%9c%89%e6%95%88%e4%bd%86%e4%b8%8d%e5%9c%a8-allow-403" class="header-anchor"&gt;&lt;/a&gt;③ consumer2 的 Key（有效但不在 allow）→ 403&#10;&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt" id="hl-15-1"&gt;&lt;a class="lnlinks" href="#hl-15-1"&gt;1&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-15-2"&gt;&lt;a class="lnlinks" href="#hl-15-2"&gt;2&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-15-3"&gt;&lt;a class="lnlinks" href="#hl-15-3"&gt;3&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-15-4"&gt;&lt;a class="lnlinks" href="#hl-15-4"&gt;4&lt;/a&gt;&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;HTTP/1.1 403 Forbidden&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;www-authenticate: Key realm=MSE Gateway&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Request denied by Key Auth check. Unauthorized consumer.&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;h3 id="-假-key--403不是-401"&gt;&lt;a href="#-%e5%81%87-key--403%e4%b8%8d%e6%98%af-401" class="header-anchor"&gt;&lt;/a&gt;④ 假 Key → 403（不是 401！）&#10;&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt" id="hl-16-1"&gt;&lt;a class="lnlinks" href="#hl-16-1"&gt;1&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-16-2"&gt;&lt;a class="lnlinks" href="#hl-16-2"&gt;2&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-16-3"&gt;&lt;a class="lnlinks" href="#hl-16-3"&gt;3&lt;/a&gt;&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;HTTP/1.1 403 Forbidden&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Request denied by Key Auth check. Unauthorized consumer.&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;credential 在 consumers 里查不到，与&amp;quot;不在白名单&amp;quot;走同一个分支（&lt;code&gt;main.go:288-292&lt;/code&gt;）。&lt;/p&gt;&#10;&lt;h3 id="-host-不匹配--404"&gt;&lt;a href="#-host-%e4%b8%8d%e5%8c%b9%e9%85%8d--404" class="header-anchor"&gt;&lt;/a&gt;⑤ Host 不匹配 → 404&#10;&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt" id="hl-17-1"&gt;&lt;a class="lnlinks" href="#hl-17-1"&gt;1&lt;/a&gt;&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -s -i http://localhost:10000/get &lt;span class="c1"&gt;# 不带 Host 头&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt" id="hl-18-1"&gt;&lt;a class="lnlinks" href="#hl-18-1"&gt;1&lt;/a&gt;&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;HTTP/1.1 404 Not Found&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;请求根本没命中 &lt;code&gt;httpbin.example.com&lt;/code&gt; 这个 virtual host，路由不存在，与插件无关。&lt;/p&gt;&#10;&lt;h2 id="六踩坑记录"&gt;&lt;a href="#%e5%85%ad%e8%b8%a9%e5%9d%91%e8%ae%b0%e5%bd%95" class="header-anchor"&gt;&lt;/a&gt;六、踩坑记录&#10;&lt;/h2&gt;&lt;ol&gt;&#10;&lt;li&gt;&#10;&lt;p&gt;&lt;strong&gt;官方 Envoy 加载不了 Go 编译的插件&lt;/strong&gt;：报 &lt;code&gt;Missing or unknown Proxy-Wasm ABI version&lt;/code&gt;。原因是 Higress Go SDK 导出 &lt;code&gt;proxy_abi_version_0_2_100&lt;/code&gt;，官方 Envoy 只认 &lt;code&gt;0_2_1&lt;/code&gt;。解法：换 &lt;code&gt;higress/gateway&lt;/code&gt; 镜像。（详见 3.2）&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;&#10;&lt;p&gt;&lt;strong&gt;&lt;code&gt;docker pull&lt;/code&gt; OCI wasm 插件镜像报 &lt;code&gt;unsupported media type application/vnd.oci.empty.v1+json&lt;/code&gt;&lt;/strong&gt;：wasm 插件镜像是 &lt;code&gt;FROM scratch&lt;/code&gt; + OCI 空层的纯 artifact，Docker 旧存储驱动不支持。解法：要么在 Docker Desktop 设置里启用 containerd 存储，要么绕过 daemon 直接 curl registry API 下载 blob（见 4.2 备选方案）。&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;&#10;&lt;p&gt;&lt;strong&gt;docker.io 拉取超时&lt;/strong&gt;：国内直连 docker.io 经常不通。阿里云 registry 不受影响；其余镜像走加速器或代理。&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;&#10;&lt;p&gt;&lt;strong&gt;原始文档的错误&lt;/strong&gt;：网上流传的该 YAML 讲解中&amp;quot;假 Key 返回 401&amp;quot;的说法与 Go 实现不符，实际是 403。读插件源码（&lt;code&gt;main.go&lt;/code&gt; 的四个 &lt;code&gt;denied*&lt;/code&gt; 函数）是最可靠的事实来源。&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;h2 id="七常用命令速查"&gt;&lt;a href="#%e4%b8%83%e5%b8%b8%e7%94%a8%e5%91%bd%e4%bb%a4%e9%80%9f%e6%9f%a5" class="header-anchor"&gt;&lt;/a&gt;七、常用命令速查&#10;&lt;/h2&gt;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt" id="hl-19-1"&gt;&lt;a class="lnlinks" href="#hl-19-1"&gt; 1&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-19-2"&gt;&lt;a class="lnlinks" href="#hl-19-2"&gt; 2&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-19-3"&gt;&lt;a class="lnlinks" href="#hl-19-3"&gt; 3&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-19-4"&gt;&lt;a class="lnlinks" href="#hl-19-4"&gt; 4&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-19-5"&gt;&lt;a class="lnlinks" href="#hl-19-5"&gt; 5&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-19-6"&gt;&lt;a class="lnlinks" href="#hl-19-6"&gt; 6&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-19-7"&gt;&lt;a class="lnlinks" href="#hl-19-7"&gt; 7&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-19-8"&gt;&lt;a class="lnlinks" href="#hl-19-8"&gt; 8&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-19-9"&gt;&lt;a class="lnlinks" href="#hl-19-9"&gt; 9&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-19-10"&gt;&lt;a class="lnlinks" href="#hl-19-10"&gt;10&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-19-11"&gt;&lt;a class="lnlinks" href="#hl-19-11"&gt;11&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-19-12"&gt;&lt;a class="lnlinks" href="#hl-19-12"&gt;12&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-19-13"&gt;&lt;a class="lnlinks" href="#hl-19-13"&gt;13&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-19-14"&gt;&lt;a class="lnlinks" href="#hl-19-14"&gt;14&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-19-15"&gt;&lt;a class="lnlinks" href="#hl-19-15"&gt;15&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-19-16"&gt;&lt;a class="lnlinks" href="#hl-19-16"&gt;16&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-19-17"&gt;&lt;a class="lnlinks" href="#hl-19-17"&gt;17&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-19-18"&gt;&lt;a class="lnlinks" href="#hl-19-18"&gt;18&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-19-19"&gt;&lt;a class="lnlinks" href="#hl-19-19"&gt;19&lt;/a&gt;&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; key-auth-docker-demo&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker compose up -d &lt;span class="c1"&gt;# 启动&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker compose down &lt;span class="c1"&gt;# 停止并删除容器&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker compose restart envoy &lt;span class="c1"&gt;# 改了 envoy.yaml / 插件配置后重启&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker compose logs -f envoy &lt;span class="c1"&gt;# 看日志（含插件日志，如 consumer &amp;#34;consumer1&amp;#34; authenticated）&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;bash test.sh &lt;span class="c1"&gt;# 一键跑 5 个验证场景&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 修改插件代码后重新编译（在仓库根目录执行）&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; citic-wasm-plugin/key-auth &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; docker run --rm -v &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$PWD&lt;/span&gt;&lt;span class="s2"&gt;:/src&amp;#34;&lt;/span&gt; -w /src &lt;span class="se"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -v gomodcache:/go/pkg/mod -v gocache:/root/.cache/go-build &lt;span class="se"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -e &lt;span class="nv"&gt;GOOS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;wasip1 -e &lt;span class="nv"&gt;GOARCH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;wasm -e &lt;span class="nv"&gt;GOPROXY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https://goproxy.cn,direct &lt;span class="se"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; golang:1.24 go build -buildmode&lt;span class="o"&gt;=&lt;/span&gt;c-shared -o plugin.wasm . &lt;span class="se"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; cp plugin.wasm ../../key-auth-docker-demo/ &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd&lt;/span&gt; ../../key-auth-docker-demo &lt;span class="se"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; docker compose restart envoy&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# envoy admin 接口（调试）&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl localhost:9901/config_dump &lt;span class="p"&gt;|&lt;/span&gt; python3 -m json.tool &lt;span class="p"&gt;|&lt;/span&gt; head -50&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl localhost:9901/stats &lt;span class="p"&gt;|&lt;/span&gt; grep wasm&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;h2 id="附单元测试路线不需要-docker"&gt;&lt;a href="#%e9%99%84%e5%8d%95%e5%85%83%e6%b5%8b%e8%af%95%e8%b7%af%e7%ba%bf%e4%b8%8d%e9%9c%80%e8%a6%81-docker" class="header-anchor"&gt;&lt;/a&gt;附：单元测试路线（不需要 Docker）&#10;&lt;/h2&gt;&lt;p&gt;如果只想快速验证插件逻辑的正确性，不追求真实 HTTP 效果，可以直接跑仓库里自带的单元测试（&lt;code&gt;main_test.go&lt;/code&gt;，基于 wasm-go 的 host 模拟框架，wazero 在进程内跑 wasm，不需要 Docker）：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt" id="hl-20-1"&gt;&lt;a class="lnlinks" href="#hl-20-1"&gt;1&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-20-2"&gt;&lt;a class="lnlinks" href="#hl-20-2"&gt;2&lt;/a&gt;&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; citic-wasm-plugin/key-auth&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;go &lt;span class="nb"&gt;test&lt;/span&gt; ./... -v&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;它覆盖了配置解析（合法/非法配置）、Header/Query 取 Key、401/403 分支、X-Mse-Consumer 注入等场景。但它验证的是&amp;quot;插件内部逻辑&amp;rdquo;，而不是&amp;quot;网关端到端行为&amp;quot;——后者还是要靠本文的 Docker 方案。&lt;/p&gt;&#10;</description></item><item><title>Higress 02：key-auth 插件原理与代码导读（结合 docker demo 实战）</title><link>https://saflyer.cn/posts/higress-keyauth-code-walkthrough/</link><pubDate>Thu, 17 Sep 2026 22:15:51 +0800</pubDate><guid>https://saflyer.cn/posts/higress-keyauth-code-walkthrough/</guid><description>&lt;img src="https://saflyer.cn/posts/higress-keyauth-code-walkthrough/cover.webp" alt="Featured image of post Higress 02：key-auth 插件原理与代码导读（结合 docker demo 实战）" /&gt;&#10; &lt;blockquote&gt;&#10; &lt;p&gt;导读对象有两份，对照着看：&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;strong&gt;代码&lt;/strong&gt;：&lt;code&gt;citic-wasm-plugin/key-auth/main.go&lt;/code&gt;（364 行，Higress 官方 key-auth 移植版）&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;Demo&lt;/strong&gt;：&lt;code&gt;higress/key-auth-docker-demo/&lt;/code&gt;（docker-compose.yaml + envoy.yaml + plugin.wasm + test.sh）&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;Demo 的价值：&lt;strong&gt;它绕开 K8s 和 Higress 控制面，用裸 Envoy 直接加载插件&lt;/strong&gt;，于是&amp;quot;插件挂在哪、配置长什么样、每个分支对应什么现象&amp;quot;全部看得见摸得着。读完这两份，Wasm 插件从部署到代码的整条链路就通了。&lt;/p&gt;&#10;&lt;p&gt;第 1 章是&lt;strong&gt;所有 Higress Wasm 插件通用的运行原理&lt;/strong&gt;，提炼自《ai-security-guard插件实现原理.md》的 §2~§4、§13——原理是相通的，这里做了 key-auth 视角的对照改写；两篇文档互为补充。&lt;/p&gt;&#10;&#10; &lt;/blockquote&gt;&#10;&lt;h2 id="0-demo-全景一条请求的全部旅程"&gt;&lt;a href="#0-demo-%e5%85%a8%e6%99%af%e4%b8%80%e6%9d%a1%e8%af%b7%e6%b1%82%e7%9a%84%e5%85%a8%e9%83%a8%e6%97%85%e7%a8%8b" class="header-anchor"&gt;&lt;/a&gt;0. Demo 全景：一条请求的全部旅程&#10;&lt;/h2&gt;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt" id="hl-0-1"&gt;&lt;a class="lnlinks" href="#hl-0-1"&gt; 1&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-0-2"&gt;&lt;a class="lnlinks" href="#hl-0-2"&gt; 2&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-0-3"&gt;&lt;a class="lnlinks" href="#hl-0-3"&gt; 3&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-0-4"&gt;&lt;a class="lnlinks" href="#hl-0-4"&gt; 4&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-0-5"&gt;&lt;a class="lnlinks" href="#hl-0-5"&gt; 5&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-0-6"&gt;&lt;a class="lnlinks" href="#hl-0-6"&gt; 6&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-0-7"&gt;&lt;a class="lnlinks" href="#hl-0-7"&gt; 7&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-0-8"&gt;&lt;a class="lnlinks" href="#hl-0-8"&gt; 8&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-0-9"&gt;&lt;a class="lnlinks" href="#hl-0-9"&gt; 9&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-0-10"&gt;&lt;a class="lnlinks" href="#hl-0-10"&gt;10&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-0-11"&gt;&lt;a class="lnlinks" href="#hl-0-11"&gt;11&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-0-12"&gt;&lt;a class="lnlinks" href="#hl-0-12"&gt;12&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-0-13"&gt;&lt;a class="lnlinks" href="#hl-0-13"&gt;13&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-0-14"&gt;&lt;a class="lnlinks" href="#hl-0-14"&gt;14&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-0-15"&gt;&lt;a class="lnlinks" href="#hl-0-15"&gt;15&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-0-16"&gt;&lt;a class="lnlinks" href="#hl-0-16"&gt;16&lt;/a&gt;&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl → localhost:10000&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ▼ docker-compose 里的两个容器&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ┌─────────────────────────────────────────┐&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ envoy 容器（higress/gateway:v2.2.4） │&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ 监听 :10000 │&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ listener → HttpConnectionManager │&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ → 路由匹配（Host + 路径） │&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ → wasm filter（加载 plugin.wasm）★ │ ← key-auth 代码在这里跑&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ → router filter │&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ → 转发到 cluster httpbin │&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; └───────────────┬─────────────────────────┘&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ▼&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ┌─────────────────────────────────────────┐&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ httpbin 容器（go-httpbin，:8080） │ ← 后端，回显请求信息&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; └─────────────────────────────────────────┘&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;四个文件各管一件事：&lt;/p&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;文件&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;作用&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;docker-compose.yaml&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;起两个容器，把 envoy.yaml 和 plugin.wasm 挂进 envoy 容器，10000 端口暴露给宿主机&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;envoy.yaml&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Envoy 的全部配置：监听、路由、&lt;strong&gt;wasm filter 及其配置&lt;/strong&gt;、后端 cluster&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;plugin.wasm&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;key-auth 的编译产物（&lt;code&gt;GOOS=wasip1 GOARCH=wasm go build -buildmode=c-shared&lt;/code&gt;）&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;test.sh&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;5 个 curl 场景，每个对应代码里一个分支&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;h2 id="1-wasm-插件通用运行原理与-ai-security-guard-相通"&gt;&lt;a href="#1-wasm-%e6%8f%92%e4%bb%b6%e9%80%9a%e7%94%a8%e8%bf%90%e8%a1%8c%e5%8e%9f%e7%90%86%e4%b8%8e-ai-security-guard-%e7%9b%b8%e9%80%9a" class="header-anchor"&gt;&lt;/a&gt;1. Wasm 插件通用运行原理（与 ai-security-guard 相通）&#10;&lt;/h2&gt;&#10; &lt;blockquote&gt;&#10; &lt;p&gt;本章机制对&lt;strong&gt;所有&lt;/strong&gt; Higress Wasm 插件都成立。每节分「通俗理解」与「技术实现」两层，技术实现层都给出 key-auth 的具体对照。&lt;/p&gt;&#10;&#10; &lt;/blockquote&gt;&#10;&lt;h3 id="11-地基沙箱hostcall-与每线程一个-vm"&gt;&lt;a href="#11-%e5%9c%b0%e5%9f%ba%e6%b2%99%e7%ae%b1hostcall-%e4%b8%8e%e6%af%8f%e7%ba%bf%e7%a8%8b%e4%b8%80%e4%b8%aa-vm" class="header-anchor"&gt;&lt;/a&gt;1.1 地基：沙箱、hostcall 与&amp;quot;每线程一个 VM&amp;quot;&#10;&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;通俗理解&lt;/strong&gt;&lt;/p&gt;&#10;&lt;p&gt;Higress 的数据面是 Envoy，给它加功能传统上要改 C++ 源码重新编译——门槛高、风险大。Wasm（WebAssembly）换了个思路：插件编译成 &lt;code&gt;.wasm&lt;/code&gt; 二进制，运行在 Envoy 内部的&lt;strong&gt;沙箱虚拟机&lt;/strong&gt;里。&lt;/p&gt;&#10;&lt;p&gt;打个比方：Envoy 是一栋大楼，Wasm 插件是楼里的&amp;quot;租客&amp;quot;——住在隔音隔热的房间里（沙箱），&lt;strong&gt;崩溃不会拖垮大楼，也不能随便动楼里的东西&lt;/strong&gt;；但大楼给租客留了一部&lt;strong&gt;服务电话&lt;/strong&gt;，想做什么就打电话让前台代办。这部电话就是 hostcall（宿主调用）。&lt;/p&gt;&#10;&lt;p&gt;还要注意：大楼里有很多前台窗口（Envoy 多 worker 线程），&lt;strong&gt;每个窗口都配一个一模一样的租客&lt;/strong&gt;（每线程一个独立 Wasm VM）。所以插件不能指望&amp;quot;上一次请求存在内存里的东西这次还在&amp;quot;——你可能换了窗口、换了租客。这就是插件里不能存跨请求全局状态的原因。&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;技术实现&lt;/strong&gt;&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&#10;&lt;p&gt;&lt;code&gt;plugin.wasm&lt;/code&gt; 作为 Envoy 的 Http Filter 加载，Envoy 每个 worker 线程实例化一个 Wasm VM 执行它。demo 里 envoy 启动参数带 &lt;code&gt;--concurrency 1&lt;/code&gt;（单线程），就是排除多 VM 副本的干扰。&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;&#10;&lt;p&gt;插件的一切能力来自 proxy-wasm ABI 的 hostcall。key-auth 只用了 3 个，是&amp;quot;最瘦&amp;quot;的一类插件：&lt;/p&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;插件想干什么&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;hostcall&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;代码位置&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;读请求头（取 token）&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;GetHttpRequestHeader&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;main.go:263&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;注入消费者头&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;AddHttpRequestHeader&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;main.go:294&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;直接给客户端回拒绝响应&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;SendHttpResponseWithDetail&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;main.go:330&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;p&gt;对照 ai-security-guard，它还需要 &lt;code&gt;GetHttpRequestBody&lt;/code&gt;（读 body）、&lt;code&gt;DispatchHttpCall&lt;/code&gt;（外呼安全服务）、&lt;code&gt;ResumeHttpRequest&lt;/code&gt;（异步放行）、&lt;code&gt;InjectEncodedDataToFilterChain&lt;/code&gt;（流式注入）等——插件要干的事越多，用到的 hostcall 越多。&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;&#10;&lt;p&gt;跨请求状态的处理原则：&lt;strong&gt;要么不存，要么建在配置期&lt;/strong&gt;。&lt;code&gt;credential2Name&lt;/code&gt;（main.go:130）这种&amp;quot;配置不变它就不变&amp;quot;的表，在 &lt;code&gt;parseGlobalConfig&lt;/code&gt; 时每个 VM 各建一份；而请求级状态（本次认出的 consumer 名）不落 VM 内存，而是注入 &lt;code&gt;X-Mse-Consumer&lt;/code&gt; 请求头随请求往下游走。&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;&#10;&lt;p&gt;ABI 版本注意：Go 编译的插件导出 &lt;code&gt;proxy_abi_version_0_2_100&lt;/code&gt; 扩展 ABI，&lt;strong&gt;只有 Higress fork 的 Envoy 支持&lt;/strong&gt;，官方 Envoy 加载即失败（demo 用 &lt;code&gt;higress/gateway&lt;/code&gt; 镜像的原因；详解见《key-auth插件本地Docker部署与运行机制指南.md》§3.2）。&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h3 id="12-骨架wrapper-框架与回调注册"&gt;&lt;a href="#12-%e9%aa%a8%e6%9e%b6wrapper-%e6%a1%86%e6%9e%b6%e4%b8%8e%e5%9b%9e%e8%b0%83%e6%b3%a8%e5%86%8c" class="header-anchor"&gt;&lt;/a&gt;1.2 骨架：wrapper 框架与回调注册&#10;&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;通俗理解&lt;/strong&gt;&lt;/p&gt;&#10;&lt;p&gt;一次请求在网关里要过几个&amp;quot;检查站&amp;quot;，插件在 &lt;code&gt;init()&lt;/code&gt; 里登记了每个站点要干什么。wasm-go 的 &lt;code&gt;wrapper&lt;/code&gt; 包还帮插件干了两件麻烦事：一是&lt;strong&gt;把分片到达的 body 拼完整再交给你&lt;/strong&gt;（裸 SDK 里要自己拼）；二是&lt;strong&gt;根据请求命中的配置级别&lt;/strong&gt;（全局/域名/路由）把对应配置递给你的回调。&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;技术实现&lt;/strong&gt;&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;插件执行阶段与优先级由代码注解声明：key-auth 是 &lt;code&gt;@Phase AUTHN @Priority 321&lt;/code&gt;（main.go:62-63）。多插件并存时这决定执行顺序——认证（AUTHN 阶段）天然排在业务类插件（默认阶段）前面，先过门禁再谈别的。&lt;/li&gt;&#10;&lt;li&gt;&lt;code&gt;wrapper.SetCtx&lt;/code&gt; 注册回调（main.go:37-43，详见 §3）。key-auth 只挂两类：&lt;code&gt;ParseOverrideConfigBy&lt;/code&gt;（配置解析）+ &lt;code&gt;ProcessRequestHeadersBy&lt;/code&gt;（请求头处理），因为认证只看请求头就够了；ai-security-guard 要挂 5~6 类（还要读请求体、拦响应），复杂度差别就在这。&lt;/li&gt;&#10;&lt;li&gt;wrapper 的配置分层机制：顶层 JSON 给 &lt;code&gt;parseGlobalConfig&lt;/code&gt;，&lt;code&gt;_rules_&lt;/code&gt; 数组里每条给 &lt;code&gt;parseOverrideRuleConfig&lt;/code&gt;；请求期框架按请求命中的路由/域名，把对应的那份配置递给回调。这套机制的&amp;quot;实体&amp;quot;见 §2.3 对 envoy.yaml:42 的剖析。&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h3 id="13-异步模型actionpause--resume以及-key-auth-的同步特权"&gt;&lt;a href="#13-%e5%bc%82%e6%ad%a5%e6%a8%a1%e5%9e%8bactionpause--resume%e4%bb%a5%e5%8f%8a-key-auth-%e7%9a%84%e5%90%8c%e6%ad%a5%e7%89%b9%e6%9d%83" class="header-anchor"&gt;&lt;/a&gt;1.3 异步模型：ActionPause / Resume，以及 key-auth 的&amp;quot;同步特权&amp;quot;&#10;&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;通俗理解&lt;/strong&gt;&lt;/p&gt;&#10;&lt;p&gt;这是理解插件执行模型最关键的一点。Envoy 的每个线程是&lt;strong&gt;单线程事件循环&lt;/strong&gt;：插件代码运行时，这个线程上其他请求都在排队。所以插件里&lt;strong&gt;绝不允许&amp;quot;发一个网络请求然后干等结果&amp;quot;&lt;/strong&gt;——那会把整个线程卡死，全网关的请求都跟着遭殃。&lt;/p&gt;&#10;&lt;p&gt;需要外呼的插件（如 ai-security-guard）正确姿势是&amp;quot;&lt;strong&gt;过安检&lt;/strong&gt;&amp;ldquo;模式：旅客（请求）到安检口，安检员说&amp;quot;你先到旁边等着&amp;rdquo;（&lt;code&gt;ActionPause&lt;/code&gt;），安检仪扫完（安全服务返回回调），安检员看结果——没问题就喊&amp;quot;过来继续走&amp;quot;（&lt;code&gt;ResumeHttpRequest&lt;/code&gt;），有问题就直接请出去（&lt;code&gt;SendHttpResponse&lt;/code&gt;）。等待期间安检口照常处理其他旅客。&lt;/p&gt;&#10;&lt;p&gt;key-auth 没这个烦恼：它是&amp;quot;&lt;strong&gt;看一眼证件&lt;/strong&gt;&amp;ldquo;的安检员——所有判定都是内存操作（查 map、比名单），微秒级出结果，从不外呼，所以永远同步返回。&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;技术实现&lt;/strong&gt;&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;key-auth 的 &lt;code&gt;onHttpRequestHeaders&lt;/code&gt;（main.go:239）只做：读头 → map 查询 → 要么 &lt;code&gt;ActionContinue&lt;/code&gt; 放行，要么 &lt;code&gt;SendHttpResponse&lt;/code&gt; 本地拒绝后 &lt;code&gt;return ActionContinue&lt;/code&gt;（请求已被 hostcall 终止，返回值只是形式，见 §6）。&lt;/li&gt;&#10;&lt;li&gt;需要外呼的插件是另一套写法：body hook 返回 &lt;code&gt;types.ActionPause&lt;/code&gt;，在 &lt;code&gt;DispatchHttpCall&lt;/code&gt; 的回调里根据结果 &lt;code&gt;ResumeHttpRequest()&lt;/code&gt; 放行或 &lt;code&gt;SendHttpResponse()&lt;/code&gt; 拦截；同一时刻只许一次在途调用（单飞），避免结果乱序。&lt;/li&gt;&#10;&lt;li&gt;判断一个插件属于哪类的方法：看回调里有没有发起异步调用（HTTP / Redis 等）。有 → 必须 Pause/Resume；没有 → 像 key-auth 一样同步返回即可，代码简单一个数量级。&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h3 id="14-部署链路pluginwasm-怎么变成生效的插件"&gt;&lt;a href="#14-%e9%83%a8%e7%bd%b2%e9%93%be%e8%b7%afpluginwasm-%e6%80%8e%e4%b9%88%e5%8f%98%e6%88%90%e7%94%9f%e6%95%88%e7%9a%84%e6%8f%92%e4%bb%b6" class="header-anchor"&gt;&lt;/a&gt;1.4 部署链路：plugin.wasm 怎么变成生效的插件&#10;&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;通俗理解&lt;/strong&gt;&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;OCI 镜像是 wasm 的&amp;quot;快递盒&amp;rdquo;&lt;/strong&gt;：&lt;code&gt;plugin.wasm&lt;/code&gt; 只有几 MB，要送进 K8s 集群里每个网关 Pod，最省事的办法是借用现成的 Docker 镜像物流体系——装进盒子（Dockerfile COPY）、贴上单号（VERSION tag）、寄到仓库（docker push），网关填个快递单号（&lt;code&gt;url: oci://...&lt;/code&gt;）就能收到并拆包加载。&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;McpBridge 是给服务&amp;quot;铺路&amp;quot;&lt;/strong&gt;：默认 Higress 只把&amp;quot;路由用到的服务&amp;quot;下发成 Envoy cluster；路由之外的服务（比如 ai-security-guard 要外呼的安全服务）想用，就得用 McpBridge 额外注册。&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;配置生效讲&amp;quot;就近原则&amp;quot;&lt;/strong&gt;：请求命中了具体路由/域名配置就用它，完全不沾边才用 &lt;code&gt;defaultConfig&lt;/code&gt;。&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;技术实现&lt;/strong&gt;&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;code&gt;WasmPlugin.spec.url&lt;/code&gt; 的三种形态：&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;code&gt;oci://...&lt;/code&gt;：镜像分发，生产环境用（&lt;code&gt;keyauth.yaml&lt;/code&gt; 示例里的 &lt;code&gt;oci://docker.io/dongjiang1989/keyauth:1.0.0&lt;/code&gt;）；&lt;/li&gt;&#10;&lt;li&gt;&lt;code&gt;http://plugin-server:8080/...&lt;/code&gt;：standalone 部署内置插件服务，官方插件（如 key-auth 2.0.0）开箱即用；&lt;/li&gt;&#10;&lt;li&gt;&lt;code&gt;file:///...&lt;/code&gt;：本地文件挂载，demo 用的就是这种（&lt;code&gt;plugin.wasm&lt;/code&gt; 直接挂进 envoy 容器），改代码重编后重启容器即生效，最适合调试。&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;K8s 链路：&lt;code&gt;WasmPlugin&lt;/code&gt; CRD → Higress 控制面翻译成 Envoy 的 wasm filter 配置下发到 gateway。demo 里手写的 envoy.yaml:26-42 就是这份&amp;quot;翻译结果&amp;quot;的样貌（§2.2 逐行拆解）。&lt;/li&gt;&#10;&lt;li&gt;McpBridge 的两个用途：① 给路由提供上游（本例把 &lt;code&gt;httpbin.org&lt;/code&gt; 注册成 &lt;code&gt;httpbin.dns&lt;/code&gt; 供 Ingress 引用）；② 给插件外呼铺路建 cluster（key-auth 无外呼用不到；ai-security-guard 靠它获得 &lt;code&gt;outbound|443||safecheck.dns&lt;/code&gt; 这个 cluster）。&lt;/li&gt;&#10;&lt;li&gt;配置匹配的就近原则落到插件内部，就是 &lt;code&gt;_rules_&lt;/code&gt; / &lt;code&gt;_match_route_&lt;/code&gt; 的命中逻辑（§2.3、§4.3）。&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h2 id="2-envoyyaml插件是怎么挂进请求链路的"&gt;&lt;a href="#2-envoyyaml%e6%8f%92%e4%bb%b6%e6%98%af%e6%80%8e%e4%b9%88%e6%8c%82%e8%bf%9b%e8%af%b7%e6%b1%82%e9%93%be%e8%b7%af%e7%9a%84" class="header-anchor"&gt;&lt;/a&gt;2. envoy.yaml：插件是怎么&amp;quot;挂&amp;quot;进请求链路的&#10;&lt;/h2&gt;&lt;p&gt;K8s 里我们写 WasmPlugin CRD，控制面把它翻译成 Envoy 配置；demo 里没有控制面，&lt;strong&gt;这段 envoy.yaml 就是翻译后的最终产物&lt;/strong&gt;，所以看它最能理解插件的真实形态。&lt;/p&gt;&#10;&lt;h3 id="21-路由层envoyyaml14-25"&gt;&lt;a href="#21-%e8%b7%af%e7%94%b1%e5%b1%82envoyyaml14-25" class="header-anchor"&gt;&lt;/a&gt;2.1 路由层（envoy.yaml:14-25）&#10;&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt" id="hl-1-1"&gt;&lt;a class="lnlinks" href="#hl-1-1"&gt;1&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-1-2"&gt;&lt;a class="lnlinks" href="#hl-1-2"&gt;2&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-1-3"&gt;&lt;a class="lnlinks" href="#hl-1-3"&gt;3&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-1-4"&gt;&lt;a class="lnlinks" href="#hl-1-4"&gt;4&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-1-5"&gt;&lt;a class="lnlinks" href="#hl-1-5"&gt;5&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-1-6"&gt;&lt;a class="lnlinks" href="#hl-1-6"&gt;6&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-1-7"&gt;&lt;a class="lnlinks" href="#hl-1-7"&gt;7&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-1-8"&gt;&lt;a class="lnlinks" href="#hl-1-8"&gt;8&lt;/a&gt;&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;route_config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;virtual_hosts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;httpbin&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;domains&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="l"&gt;httpbin.example.com] &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# Host 头必须匹配&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;keyauth-route &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# ★ 路由名，插件配置里引用它&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;match&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;{&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;/ }&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;route&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;{&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;cluster&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;httpbin }&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;两个关键点：&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;code&gt;domains&lt;/code&gt; 决定哪些 Host 走这条路由——test.sh 场景⑤不带 Host 直接访问，匹配不到 virtual host，Envoy 直接回 &lt;strong&gt;404&lt;/strong&gt;，请求根本没到插件；&lt;/li&gt;&#10;&lt;li&gt;路由名 &lt;code&gt;keyauth-route&lt;/code&gt; 是给插件配置引用的&amp;quot;身份证号&amp;quot;，马上会看到。&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h3 id="22-wasm-filter-层envoyyaml26-42全篇最重要的一段"&gt;&lt;a href="#22-wasm-filter-%e5%b1%82envoyyaml26-42%e5%85%a8%e7%af%87%e6%9c%80%e9%87%8d%e8%a6%81%e7%9a%84%e4%b8%80%e6%ae%b5" class="header-anchor"&gt;&lt;/a&gt;2.2 wasm filter 层（envoy.yaml:26-42）——全篇最重要的一段&#10;&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt" id="hl-2-1"&gt;&lt;a class="lnlinks" href="#hl-2-1"&gt; 1&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-2-2"&gt;&lt;a class="lnlinks" href="#hl-2-2"&gt; 2&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-2-3"&gt;&lt;a class="lnlinks" href="#hl-2-3"&gt; 3&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-2-4"&gt;&lt;a class="lnlinks" href="#hl-2-4"&gt; 4&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-2-5"&gt;&lt;a class="lnlinks" href="#hl-2-5"&gt; 5&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-2-6"&gt;&lt;a class="lnlinks" href="#hl-2-6"&gt; 6&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-2-7"&gt;&lt;a class="lnlinks" href="#hl-2-7"&gt; 7&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-2-8"&gt;&lt;a class="lnlinks" href="#hl-2-8"&gt; 8&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-2-9"&gt;&lt;a class="lnlinks" href="#hl-2-9"&gt; 9&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-2-10"&gt;&lt;a class="lnlinks" href="#hl-2-10"&gt;10&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-2-11"&gt;&lt;a class="lnlinks" href="#hl-2-11"&gt;11&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-2-12"&gt;&lt;a class="lnlinks" href="#hl-2-12"&gt;12&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-2-13"&gt;&lt;a class="lnlinks" href="#hl-2-13"&gt;13&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-2-14"&gt;&lt;a class="lnlinks" href="#hl-2-14"&gt;14&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-2-15"&gt;&lt;a class="lnlinks" href="#hl-2-15"&gt;15&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-2-16"&gt;&lt;a class="lnlinks" href="#hl-2-16"&gt;16&lt;/a&gt;&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;http_filters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;envoy.filters.http.wasm&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;typed_config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;&amp;#34;@type&amp;#34;: &lt;/span&gt;&lt;span class="l"&gt;.../filters.http.wasm.v3.Wasm&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;key-auth &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 对应 wrapper.SetCtx(&amp;#34;key-auth&amp;#34;, ...)&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;vm_config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;runtime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;envoy.wasm.runtime.v8 &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# V8 沙箱里跑 wasm&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;code&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;local&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;{&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;/etc/envoy/plugin.wasm }&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;&amp;#34;@type&amp;#34;: &lt;/span&gt;&lt;span class="l"&gt;type.googleapis.com/google.protobuf.StringValue&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;gt;-&lt;/span&gt;&lt;span class="sd"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; {&amp;#34;consumers&amp;#34;:[...],&amp;#34;keys&amp;#34;:[&amp;#34;x-api-key&amp;#34;,&amp;#34;apikey&amp;#34;],&amp;#34;in_header&amp;#34;:true,&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; &amp;#34;global_auth&amp;#34;:false,&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; &amp;#34;_rules_&amp;#34;:[{&amp;#34;_match_route_&amp;#34;:[&amp;#34;keyauth-route&amp;#34;],&amp;#34;allow&amp;#34;:[&amp;#34;consumer1&amp;#34;]}]}&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;逐行拆解：&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;strong&gt;http_filters 的顺序就是请求的处理顺序&lt;/strong&gt;：wasm 排在 router 前面，所以插件先执行，拦下的请求根本不会转发给 httpbin。&lt;/li&gt;&#10;&lt;li&gt;&lt;code&gt;configuration.value&lt;/code&gt; 是一段 JSON 字符串——&lt;strong&gt;这就是插件 &lt;code&gt;parseGlobalConfig&lt;/code&gt;/&lt;code&gt;parseOverrideRuleConfig&lt;/code&gt; 收到的原始 &lt;code&gt;gjson.Result&lt;/code&gt;&lt;/strong&gt;。&lt;/li&gt;&#10;&lt;li&gt;注意它的结构：全局字段（consumers/keys/in_header/global_auth）平铺在顶层，路由级规则放在 &lt;strong&gt;&lt;code&gt;_rules_&lt;/code&gt;&lt;/strong&gt; 数组里，每条用 &lt;strong&gt;&lt;code&gt;_match_route_&lt;/code&gt;&lt;/strong&gt; 指定生效的路由名。&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h3 id="23-大揭秘_rules_-就是-wasmplugin-的-matchrules"&gt;&lt;a href="#23-%e5%a4%a7%e6%8f%ad%e7%a7%98_rules_-%e5%b0%b1%e6%98%af-wasmplugin-%e7%9a%84-matchrules" class="header-anchor"&gt;&lt;/a&gt;2.3 大揭秘：&lt;code&gt;_rules_&lt;/code&gt; 就是 WasmPlugin 的 matchRules&#10;&lt;/h3&gt;&lt;p&gt;对照 K8s 版配置（keyauth.yaml），两边其实一模一样：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt" id="hl-3-1"&gt;&lt;a class="lnlinks" href="#hl-3-1"&gt; 1&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-3-2"&gt;&lt;a class="lnlinks" href="#hl-3-2"&gt; 2&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-3-3"&gt;&lt;a class="lnlinks" href="#hl-3-3"&gt; 3&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-3-4"&gt;&lt;a class="lnlinks" href="#hl-3-4"&gt; 4&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-3-5"&gt;&lt;a class="lnlinks" href="#hl-3-5"&gt; 5&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-3-6"&gt;&lt;a class="lnlinks" href="#hl-3-6"&gt; 6&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-3-7"&gt;&lt;a class="lnlinks" href="#hl-3-7"&gt; 7&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-3-8"&gt;&lt;a class="lnlinks" href="#hl-3-8"&gt; 8&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-3-9"&gt;&lt;a class="lnlinks" href="#hl-3-9"&gt; 9&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-3-10"&gt;&lt;a class="lnlinks" href="#hl-3-10"&gt;10&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-3-11"&gt;&lt;a class="lnlinks" href="#hl-3-11"&gt;11&lt;/a&gt;&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# K8s 里你写的 # Envoy 实际收到的（envoy.yaml:42）&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;defaultConfig&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;──合并──► 顶层字段：consumers/keys/in_header/global_auth&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;consumers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="l"&gt;...]&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;global_auth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;...&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;matchRules&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;──合并──► &amp;#34;_rules_&amp;#34;: [{&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;ingress&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;→ &amp;#34;_match_route_&amp;#34;: [&amp;#34;keyauth-route&amp;#34;],&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;ingress-keyauth-httpbin &amp;#34;allow&amp;#34;: [&amp;#34;consumer1&amp;#34;] }]&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;allow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="l"&gt;consumer1]&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;Higress 控制面干的事就是把 &lt;code&gt;defaultConfig + matchRules&lt;/code&gt; 编译成这样一段 JSON（K8s 里 &lt;code&gt;_match_route_&lt;/code&gt; 的值是 ingress 名，demo 里是手写的 envoy 路由名）。wasm-go wrapper 收到后：顶层 JSON 给 &lt;code&gt;parseGlobalConfig&lt;/code&gt;，&lt;code&gt;_rules_&lt;/code&gt; 里每条给 &lt;code&gt;parseOverrideRuleConfig&lt;/code&gt;。&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;&amp;ldquo;两级配置机制&amp;quot;的抽象说法，在 envoy.yaml:42 这一行里能看到实体。&lt;/strong&gt;&lt;/p&gt;&#10;&lt;h3 id="24-docker-compose-里的两个细节"&gt;&lt;a href="#24-docker-compose-%e9%87%8c%e7%9a%84%e4%b8%a4%e4%b8%aa%e7%bb%86%e8%8a%82" class="header-anchor"&gt;&lt;/a&gt;2.4 docker-compose 里的两个细节&#10;&lt;/h3&gt;&lt;ul&gt;&#10;&lt;li&gt;envoy 镜像是 &lt;code&gt;higress/gateway&lt;/code&gt;（compose 文件第 11 行注释）：Go 编译的插件用了 &lt;code&gt;proxy_abi_version_0_2_100&lt;/code&gt; 这个 ABI 扩展，&lt;strong&gt;只有 Higress fork 的 Envoy 支持&lt;/strong&gt;，官方 Envoy 加载会失败（见 §1.1）。&lt;/li&gt;&#10;&lt;li&gt;&lt;code&gt;--concurrency 1&lt;/code&gt;：单 worker 线程。回忆 §1.1&amp;quot;每个 worker 线程一个独立 Wasm VM&amp;rdquo;——demo 设成 1，保证你看到的日志和行为都来自同一个 VM，排除多副本干扰。&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h2 id="3-代码骨架maingo35-43"&gt;&lt;a href="#3-%e4%bb%a3%e7%a0%81%e9%aa%a8%e6%9e%b6maingo35-43" class="header-anchor"&gt;&lt;/a&gt;3. 代码骨架（main.go:35-43）&#10;&lt;/h2&gt;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt" id="hl-4-1"&gt;&lt;a class="lnlinks" href="#hl-4-1"&gt;1&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-4-2"&gt;&lt;a class="lnlinks" href="#hl-4-2"&gt;2&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-4-3"&gt;&lt;a class="lnlinks" href="#hl-4-3"&gt;3&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-4-4"&gt;&lt;a class="lnlinks" href="#hl-4-4"&gt;4&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-4-5"&gt;&lt;a class="lnlinks" href="#hl-4-5"&gt;5&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-4-6"&gt;&lt;a class="lnlinks" href="#hl-4-6"&gt;6&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-4-7"&gt;&lt;a class="lnlinks" href="#hl-4-7"&gt;7&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-4-8"&gt;&lt;a class="lnlinks" href="#hl-4-8"&gt;8&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-4-9"&gt;&lt;a class="lnlinks" href="#hl-4-9"&gt;9&lt;/a&gt;&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&#9;&lt;/span&gt;&lt;span class="nx"&gt;wrapper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SetCtx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&#9;&#9;&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;key-auth&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// ← 对应 envoy.yaml 的 name: key-auth&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&#9;&#9;&lt;/span&gt;&lt;span class="nx"&gt;wrapper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ParseOverrideConfigBy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;parseGlobalConfig&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;parseOverrideRuleConfig&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&#9;&#9;&lt;/span&gt;&lt;span class="nx"&gt;wrapper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ProcessRequestHeadersBy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;onHttpRequestHeaders&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&#9;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;Wasm 插件没有进程入口，&lt;code&gt;main()&lt;/code&gt; 是编译占位；真正的入口是 &lt;code&gt;init()&lt;/code&gt;——插件加载时向 wrapper 框架挂号三件事：&lt;strong&gt;我的名字&lt;/strong&gt;（要和 envoy.yaml / WasmPlugin 里的 name 对上）、&lt;strong&gt;配置来了调谁&lt;/strong&gt;、&lt;strong&gt;请求头到了调谁&lt;/strong&gt;。之后所有代码都是被动回调。&lt;/p&gt;&#10;&lt;p&gt;只挂两个 hook，因为认证只看请求头就够了。对比 ai-security-guard 挂 6 个（要读 body、拦响应），复杂度差别就在这（见 §1.2）。&lt;/p&gt;&#10;&lt;h2 id="4-配置解析把-envoyyaml42-那段-json-变成内存结构"&gt;&lt;a href="#4-%e9%85%8d%e7%bd%ae%e8%a7%a3%e6%9e%90%e6%8a%8a-envoyyaml42-%e9%82%a3%e6%ae%b5-json-%e5%8f%98%e6%88%90%e5%86%85%e5%ad%98%e7%bb%93%e6%9e%84" class="header-anchor"&gt;&lt;/a&gt;4. 配置解析：把 envoy.yaml:42 那段 JSON 变成内存结构&#10;&lt;/h2&gt;&lt;h3 id="41-配置结构体maingo88-131"&gt;&lt;a href="#41-%e9%85%8d%e7%bd%ae%e7%bb%93%e6%9e%84%e4%bd%93maingo88-131" class="header-anchor"&gt;&lt;/a&gt;4.1 配置结构体（main.go:88-131）&#10;&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt" id="hl-5-1"&gt;&lt;a class="lnlinks" href="#hl-5-1"&gt;1&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-5-2"&gt;&lt;a class="lnlinks" href="#hl-5-2"&gt;2&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-5-3"&gt;&lt;a class="lnlinks" href="#hl-5-3"&gt;3&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-5-4"&gt;&lt;a class="lnlinks" href="#hl-5-4"&gt;4&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-5-5"&gt;&lt;a class="lnlinks" href="#hl-5-5"&gt;5&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-5-6"&gt;&lt;a class="lnlinks" href="#hl-5-6"&gt;6&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-5-7"&gt;&lt;a class="lnlinks" href="#hl-5-7"&gt;7&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-5-8"&gt;&lt;a class="lnlinks" href="#hl-5-8"&gt;8&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-5-9"&gt;&lt;a class="lnlinks" href="#hl-5-9"&gt;9&lt;/a&gt;&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;KeyAuthConfig&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;struct&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&#9;&lt;/span&gt;&lt;span class="nx"&gt;globalAuth&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;`yaml:&amp;#34;global_auth,omitempty&amp;#34;`&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 指针！三态的关键&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&#9;&lt;/span&gt;&lt;span class="nx"&gt;Keys&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;`yaml:&amp;#34;keys&amp;#34;`&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&#9;&lt;/span&gt;&lt;span class="nx"&gt;InQuery&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;`yaml:&amp;#34;in_query,omitempty&amp;#34;`&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&#9;&lt;/span&gt;&lt;span class="nx"&gt;InHeader&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;`yaml:&amp;#34;in_header,omitempty&amp;#34;`&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&#9;&lt;/span&gt;&lt;span class="nx"&gt;consumers&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="nx"&gt;Consumer&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&#9;&lt;/span&gt;&lt;span class="nx"&gt;allow&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&#9;&lt;/span&gt;&lt;span class="nx"&gt;credential2Name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;`yaml:&amp;#34;-&amp;#34;`&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 不从配置读，解析时自建&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;ul&gt;&#10;&lt;li&gt;&lt;strong&gt;&lt;code&gt;globalAuth&lt;/code&gt; 用 &lt;code&gt;*bool&lt;/code&gt; 指针&lt;/strong&gt;（main.go:94）：bool 零值 false 分不清&amp;quot;配了 false&amp;quot;和&amp;quot;根本没配&amp;quot;。指针区分三态：&lt;code&gt;nil&lt;/code&gt;=未设置、&lt;code&gt;&amp;amp;true&lt;/code&gt;、&lt;code&gt;&amp;amp;false&lt;/code&gt;——这是后面裁决表的基础。&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;&lt;code&gt;credential2Name&lt;/code&gt; 是运行时索引&lt;/strong&gt;（main.go:130）：配置里 consumers 是列表，逐请求遍历是 O(n)；解析时建成 &lt;code&gt;凭证→名字&lt;/code&gt; 的 map，运行时 O(1) 查表。&lt;strong&gt;配置解析一次、请求千千万万次——开销挪到解析期，是插件开发通用思路（§1.1 的状态原则）。&lt;/strong&gt;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h3 id="42-parseglobalconfig校验--建索引maingo133-204"&gt;&lt;a href="#42-parseglobalconfig%e6%a0%a1%e9%aa%8c--%e5%bb%ba%e7%b4%a2%e5%bc%95maingo133-204" class="header-anchor"&gt;&lt;/a&gt;4.2 &lt;code&gt;parseGlobalConfig&lt;/code&gt;：校验 + 建索引（main.go:133-204）&#10;&lt;/h3&gt;&lt;p&gt;收到顶层 JSON（demo 里就是 consumers/keys/in_header/global_auth 那部分），先做一串防御式校验，任一不满足整条配置被拒：&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;code&gt;keys&lt;/code&gt; 必须非空（:148-154）——没定义从哪取 key，认证无从谈起；&lt;/li&gt;&#10;&lt;li&gt;&lt;code&gt;in_query&lt;/code&gt;/&lt;code&gt;in_header&lt;/code&gt; 至少配一个（:163-165）；&lt;/li&gt;&#10;&lt;li&gt;&lt;code&gt;consumers&lt;/code&gt; 非空，name/credential 都不能空；&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;凭证不允许重复&lt;/strong&gt;（:192-194）——两个 consumer 同 key 就分不清&amp;quot;你是谁&amp;quot;，直接拒配。&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;通过后建索引。用 demo 的配置，解析完内存里是：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt" id="hl-6-1"&gt;&lt;a class="lnlinks" href="#hl-6-1"&gt;1&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-6-2"&gt;&lt;a class="lnlinks" href="#hl-6-2"&gt;2&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-6-3"&gt;&lt;a class="lnlinks" href="#hl-6-3"&gt;3&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-6-4"&gt;&lt;a class="lnlinks" href="#hl-6-4"&gt;4&lt;/a&gt;&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;credential2Name = {&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;2bda943c-ba2b-11ec-ba07-00163e1250b5&amp;#34;: &amp;#34;consumer1&amp;#34;,&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;c8c8e9ca-558e-4a2d-bb62-e700dcc40e35&amp;#34;: &amp;#34;consumer2&amp;#34;,&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;}&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&#10; &lt;blockquote&gt;&#10; &lt;p&gt;&lt;strong&gt;demo 视角&lt;/strong&gt;：改 envoy.yaml:42 里的 JSON（比如删掉 consumers），重启 compose，就能在 envoy 日志里看到对应的校验报错——配置错误在插件加载期就暴露，不会带病运行。&lt;/p&gt;&#10;&#10; &lt;/blockquote&gt;&#10;&lt;h3 id="43-parseoverrideruleconfig叠加路由级-allowmaingo206-225"&gt;&lt;a href="#43-parseoverrideruleconfig%e5%8f%a0%e5%8a%a0%e8%b7%af%e7%94%b1%e7%ba%a7-allowmaingo206-225" class="header-anchor"&gt;&lt;/a&gt;4.3 &lt;code&gt;parseOverrideRuleConfig&lt;/code&gt;：叠加路由级 allow（main.go:206-225）&#10;&lt;/h3&gt;&lt;p&gt;对 &lt;code&gt;_rules_&lt;/code&gt; 里每条规则调用一次：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt" id="hl-7-1"&gt;&lt;a class="lnlinks" href="#hl-7-1"&gt;1&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-7-2"&gt;&lt;a class="lnlinks" href="#hl-7-2"&gt;2&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-7-3"&gt;&lt;a class="lnlinks" href="#hl-7-3"&gt;3&lt;/a&gt;&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;global&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 先把全局配置整个拷过来（consumers/keys 自动继承）&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// ... 解析 allow 列表&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;ruleSet&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 标记：至少有一条路由配了插件&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;用 demo 配置：命中 &lt;code&gt;keyauth-route&lt;/code&gt; 的请求，拿到的最终 config = 全局 consumers/keys + &lt;code&gt;allow: [&amp;quot;consumer1&amp;quot;]&lt;/code&gt;；不命中任何 &lt;code&gt;_rules_&lt;/code&gt; 的请求，拿到的就是纯全局配置（&lt;code&gt;allow&lt;/code&gt; 为空）。&lt;/p&gt;&#10;&lt;p&gt;&lt;code&gt;ruleSet&lt;/code&gt; 是包级全局变量，记录&amp;quot;&lt;strong&gt;至少有一条路由配了插件&lt;/strong&gt;&amp;quot;，裁决时用来猜管理员意图（见 §5 第一步）。&lt;/p&gt;&#10;&lt;h2 id="5-核心裁决onhttprequestheadersmaingo239-327"&gt;&lt;a href="#5-%e6%a0%b8%e5%bf%83%e8%a3%81%e5%86%b3onhttprequestheadersmaingo239-327" class="header-anchor"&gt;&lt;/a&gt;5. 核心裁决：&lt;code&gt;onHttpRequestHeaders&lt;/code&gt;（main.go:239-327）&#10;&lt;/h2&gt;&lt;p&gt;请求头到达时框架调它。开头算四个布尔值：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt" id="hl-8-1"&gt;&lt;a class="lnlinks" href="#hl-8-1"&gt;1&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-8-2"&gt;&lt;a class="lnlinks" href="#hl-8-2"&gt;2&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-8-3"&gt;&lt;a class="lnlinks" href="#hl-8-3"&gt;3&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-8-4"&gt;&lt;a class="lnlinks" href="#hl-8-4"&gt;4&lt;/a&gt;&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;noAllow&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;allow&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 当前请求没命中任何 _rules_&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;globalAuthNoSet&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;globalAuth&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;globalAuthSetTrue&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;globalAuthNoSet&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;globalAuth&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;globalAuthSetFalse&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;globalAuthNoSet&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;globalAuth&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;h3 id="第一步要不要认证249-254"&gt;&lt;a href="#%e7%ac%ac%e4%b8%80%e6%ad%a5%e8%a6%81%e4%b8%8d%e8%a6%81%e8%ae%a4%e8%af%81249-254" class="header-anchor"&gt;&lt;/a&gt;第一步：要不要认证？（:249-254）&#10;&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt" id="hl-9-1"&gt;&lt;a class="lnlinks" href="#hl-9-1"&gt;1&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-9-2"&gt;&lt;a class="lnlinks" href="#hl-9-2"&gt;2&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-9-3"&gt;&lt;a class="lnlinks" href="#hl-9-3"&gt;3&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-9-4"&gt;&lt;a class="lnlinks" href="#hl-9-4"&gt;4&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-9-5"&gt;&lt;a class="lnlinks" href="#hl-9-5"&gt;5&lt;/a&gt;&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;globalAuthSetFalse&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;globalAuthNoSet&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ruleSet&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&#9;&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;noAllow&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&#9;&#9;&lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;types&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ActionContinue&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 不归我管，直接放行&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&#9;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;demo 配置 &lt;code&gt;global_auth: false&lt;/code&gt;：请求没命中 &lt;code&gt;keyauth-route&lt;/code&gt;（不会有，因为只有一个 virtual host；K8s 多路由场景才有意义）→ 直接放行，连 token 都不取。&lt;/p&gt;&#10;&lt;p&gt;&lt;code&gt;ruleSet&lt;/code&gt; 的作用：&lt;code&gt;global_auth&lt;/code&gt; 没设置时猜意图——任何路由都没配过插件（&lt;code&gt;ruleSet==false&lt;/code&gt;）说明想全局认证；有路由配了，说明按路由精细控制，没配的路由放行。&lt;/p&gt;&#10;&lt;h3 id="第二步取-token259-278"&gt;&lt;a href="#%e7%ac%ac%e4%ba%8c%e6%ad%a5%e5%8f%96-token259-278" class="header-anchor"&gt;&lt;/a&gt;第二步：取 token（:259-278）&#10;&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt" id="hl-10-1"&gt;&lt;a class="lnlinks" href="#hl-10-1"&gt;1&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-10-2"&gt;&lt;a class="lnlinks" href="#hl-10-2"&gt;2&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-10-3"&gt;&lt;a class="lnlinks" href="#hl-10-3"&gt;3&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-10-4"&gt;&lt;a class="lnlinks" href="#hl-10-4"&gt;4&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-10-5"&gt;&lt;a class="lnlinks" href="#hl-10-5"&gt;5&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-10-6"&gt;&lt;a class="lnlinks" href="#hl-10-6"&gt;6&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-10-7"&gt;&lt;a class="lnlinks" href="#hl-10-7"&gt;7&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-10-8"&gt;&lt;a class="lnlinks" href="#hl-10-8"&gt;8&lt;/a&gt;&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;InHeader&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&#9;&lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;range&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Keys&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// keys: [x-api-key, apikey]&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&#9;&#9;&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;proxywasm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetHttpRequestHeader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&#9;&#9;&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&#9;&#9;&#9;&lt;/span&gt;&lt;span class="nx"&gt;tokens&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&#9;&#9;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&#9;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;InQuery&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cm"&gt;/* 从 :path 的 query 参数取 */&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;demo 配置 &lt;code&gt;in_header: true&lt;/code&gt;：先试 &lt;code&gt;x-api-key&lt;/code&gt; 头，再试 &lt;code&gt;apikey&lt;/code&gt; 头。&lt;strong&gt;两个头都带就都收进 tokens&lt;/strong&gt;——下一步会当成攻击拒绝（test.sh 没演示这个，可以自己加一条 curl 验证）。&lt;/p&gt;&#10;&lt;p&gt;坑：&lt;code&gt;else if&lt;/code&gt; 意味着 &lt;code&gt;in_header&lt;/code&gt;/&lt;code&gt;in_query&lt;/code&gt; 同开时 query 永不生效（官方原版行为）。&lt;/p&gt;&#10;&lt;h3 id="第三步数量检查281-285"&gt;&lt;a href="#%e7%ac%ac%e4%b8%89%e6%ad%a5%e6%95%b0%e9%87%8f%e6%a3%80%e6%9f%a5281-285" class="header-anchor"&gt;&lt;/a&gt;第三步：数量检查（:281-285）&#10;&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt" id="hl-11-1"&gt;&lt;a class="lnlinks" href="#hl-11-1"&gt;1&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-11-2"&gt;&lt;a class="lnlinks" href="#hl-11-2"&gt;2&lt;/a&gt;&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;deniedMultiKeyAuthData&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 401 multi_key&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;deniedNoKeyAuthData&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 401 no_key&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;带多个 key 为什么拒绝？防凭证混淆——一合法一非法同时带，后端处理顺序不同可能放行非法请求。&lt;strong&gt;→ test.sh 场景①（不带 Key）命中的就是这步的 401。&lt;/strong&gt;&lt;/p&gt;&#10;&lt;h3 id="第四步认证查表认出你是谁288-294"&gt;&lt;a href="#%e7%ac%ac%e5%9b%9b%e6%ad%a5%e8%ae%a4%e8%af%81%e6%9f%a5%e8%a1%a8%e8%ae%a4%e5%87%ba%e4%bd%a0%e6%98%af%e8%b0%81288-294" class="header-anchor"&gt;&lt;/a&gt;第四步：认证——查表认出&amp;quot;你是谁&amp;quot;（:288-294）&#10;&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt" id="hl-12-1"&gt;&lt;a class="lnlinks" href="#hl-12-1"&gt;1&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-12-2"&gt;&lt;a class="lnlinks" href="#hl-12-2"&gt;2&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-12-3"&gt;&lt;a class="lnlinks" href="#hl-12-3"&gt;3&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-12-4"&gt;&lt;a class="lnlinks" href="#hl-12-4"&gt;4&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-12-5"&gt;&lt;a class="lnlinks" href="#hl-12-5"&gt;5&lt;/a&gt;&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;credential2Name&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&#9;&lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;deniedUnauthorizedConsumer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 403：凭证不在名单&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;proxywasm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddHttpRequestHeader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;X-Mse-Consumer&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;一次 map 查找完成认证。查到就把 consumer 名写进请求头往下游传——&lt;strong&gt;&lt;code&gt;X-Mse-Consumer&lt;/code&gt; 是 Higress 消费者体系的通用语言&lt;/strong&gt;，限流、ai-security-guard 的消费者级配置都读它。&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;→ test.sh 场景④（假 Key &lt;code&gt;wrong-key&lt;/code&gt;）命中的就是这步的 403。&lt;/strong&gt; 想亲眼看到注入效果：场景②通过的请求，httpbin 的 &lt;code&gt;/get&lt;/code&gt; 响应体会把收到的请求头原样回显，里面就有 &lt;code&gt;X-Mse-Consumer: consumer1&lt;/code&gt;。&lt;/p&gt;&#10;&lt;h3 id="第五步授权你能进这扇门吗299-326"&gt;&lt;a href="#%e7%ac%ac%e4%ba%94%e6%ad%a5%e6%8e%88%e6%9d%83%e4%bd%a0%e8%83%bd%e8%bf%9b%e8%bf%99%e6%89%87%e9%97%a8%e5%90%97299-326" class="header-anchor"&gt;&lt;/a&gt;第五步：授权——&amp;ldquo;你能进这扇门吗&amp;rdquo;（:299-326）&#10;&lt;/h3&gt;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;global_auth&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;路由无 allow&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;路由有 allow&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;strong&gt;true&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;凭证合法即放行&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;合法&lt;strong&gt;且&lt;/strong&gt;在 allow 里&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;strong&gt;false&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;直接放行（第一步已走掉）&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;合法且在 allow 里&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;strong&gt;未设置&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;ruleSet==false&lt;/code&gt; → 按 true；否则按 false&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;同上&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt" id="hl-13-1"&gt;&lt;a class="lnlinks" href="#hl-13-1"&gt; 1&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-13-2"&gt;&lt;a class="lnlinks" href="#hl-13-2"&gt; 2&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-13-3"&gt;&lt;a class="lnlinks" href="#hl-13-3"&gt; 3&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-13-4"&gt;&lt;a class="lnlinks" href="#hl-13-4"&gt; 4&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-13-5"&gt;&lt;a class="lnlinks" href="#hl-13-5"&gt; 5&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-13-6"&gt;&lt;a class="lnlinks" href="#hl-13-6"&gt; 6&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-13-7"&gt;&lt;a class="lnlinks" href="#hl-13-7"&gt; 7&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-13-8"&gt;&lt;a class="lnlinks" href="#hl-13-8"&gt; 8&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-13-9"&gt;&lt;a class="lnlinks" href="#hl-13-9"&gt; 9&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-13-10"&gt;&lt;a class="lnlinks" href="#hl-13-10"&gt;10&lt;/a&gt;&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;globalAuthSetTrue&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;noAllow&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;globalAuthNoSet&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;ruleSet&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&#9;&lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;authenticated&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 全局模式，凭证合法就行&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;globalAuthSetTrue&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;noAllow&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 全局模式 + allow 校验&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;globalAuthSetFalse&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;globalAuthNoSet&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ruleSet&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&#9;&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;noAllow&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&#9;&#9;&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="nf"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;allow&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;deniedUnauthorizedConsumer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&#9;&#9;&lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;authenticated&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&#9;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;demo 落在第三分支：&lt;strong&gt;→ test.sh 场景③（consumer2 的 Key，凭证合法但不在 &lt;code&gt;allow: [consumer1]&lt;/code&gt;）命中这里的 403&lt;/strong&gt;；场景②（consumer1）走到 &lt;code&gt;authenticated&lt;/code&gt; 放行。&lt;/p&gt;&#10;&lt;h2 id="6-拒绝响应怎么发maingo329-345"&gt;&lt;a href="#6-%e6%8b%92%e7%bb%9d%e5%93%8d%e5%ba%94%e6%80%8e%e4%b9%88%e5%8f%91maingo329-345" class="header-anchor"&gt;&lt;/a&gt;6. 拒绝响应怎么发（main.go:329-345）&#10;&lt;/h2&gt;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt" id="hl-14-1"&gt;&lt;a class="lnlinks" href="#hl-14-1"&gt;1&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-14-2"&gt;&lt;a class="lnlinks" href="#hl-14-2"&gt;2&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-14-3"&gt;&lt;a class="lnlinks" href="#hl-14-3"&gt;3&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-14-4"&gt;&lt;a class="lnlinks" href="#hl-14-4"&gt;4&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-14-5"&gt;&lt;a class="lnlinks" href="#hl-14-5"&gt;5&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-14-6"&gt;&lt;a class="lnlinks" href="#hl-14-6"&gt;6&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-14-7"&gt;&lt;a class="lnlinks" href="#hl-14-7"&gt;7&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-14-8"&gt;&lt;a class="lnlinks" href="#hl-14-8"&gt;8&lt;/a&gt;&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;proxywasm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SendHttpResponseWithDetail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&#9;&lt;/span&gt;&lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;StatusUnauthorized&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 401（或 403）&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&#9;&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;key-auth.no_key&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 内部 detail 码，进日志&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&#9;&lt;/span&gt;&lt;span class="nf"&gt;WWWAuthenticateHeader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;protectionSpace&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// WWW-Authenticate: Key realm=MSE Gateway&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&#9;&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="nb"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Request denied by Key Auth check...&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&#9;&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;types&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ActionContinue&lt;/span&gt;&lt;span class="w"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;&lt;code&gt;WWW-Authenticate&lt;/code&gt; 是 RFC 7235 的要求，告诉客户端&amp;quot;这里用 Key 认证&amp;quot;。&lt;strong&gt;test.sh 的 ①③④ 输出里都能看到这行响应头&lt;/strong&gt;，可以对照确认命中了哪个拒绝函数（body 文案各不相同）。&lt;/p&gt;&#10;&lt;p&gt;容易困惑的点：发了拒绝响应为什么还 &lt;code&gt;return ActionContinue&lt;/code&gt;？&lt;code&gt;SendHttpResponse&lt;/code&gt; 是&amp;quot;立即响应并终止该请求&amp;quot;的 hostcall，请求已结束，返回值只是形式。对比 ai-security-guard 外呼时的 &lt;code&gt;ActionPause&lt;/code&gt;（等异步回调，见 §1.3），语义完全不同。&lt;/p&gt;&#10;&lt;h2 id="7-剧本对照表testsh--个场景--代码分支"&gt;&lt;a href="#7-%e5%89%a7%e6%9c%ac%e5%af%b9%e7%85%a7%e8%a1%a8testsh--%e4%b8%aa%e5%9c%ba%e6%99%af--%e4%bb%a3%e7%a0%81%e5%88%86%e6%94%af" class="header-anchor"&gt;&lt;/a&gt;7. 剧本对照表：test.sh ⑤ 个场景 → 代码分支&#10;&lt;/h2&gt;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;test.sh 场景&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;请求&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;命中代码&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;期望&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;① 不带 Key&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;无认证头&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;len(tokens)&amp;lt;=0&lt;/code&gt; → &lt;code&gt;deniedNoKeyAuthData&lt;/code&gt;（:283）&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;401 no_key&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;② consumer1 的 Key&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;x-api-key: 2bda...&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;查表得 consumer1（:288）→ allow 命中（:317-322）&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;200，响应回显含 &lt;code&gt;X-Mse-Consumer: consumer1&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;③ consumer2 的 Key&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;apikey: c8c8...&lt;/code&gt;（第二个 key 名）&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;认证过、授权失败（:317-319）&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;403 unauthorized&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;④ 假 Key&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;x-api-key: wrong-key&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;查表未命中（:289-291）&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;403 unauthorized&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;⑤ 不带 Host&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;直接访问 IP:10000&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;strong&gt;到不了插件&lt;/strong&gt;：virtual host &lt;code&gt;domains&lt;/code&gt; 不匹配（envoy.yaml:18-19），Envoy 路由层 404&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;404&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;p&gt;场景⑤是&amp;quot;插件不是万能的&amp;quot;的最好示例：它只在命中路由后执行，路由都没匹配上的请求根本轮不到它。&lt;/p&gt;&#10;&#10; &lt;blockquote&gt;&#10; &lt;p&gt;想动手验证：&lt;code&gt;cd key-auth-docker-demo &amp;amp;&amp;amp; docker compose up -d &amp;amp;&amp;amp; ./test.sh&lt;/code&gt;。改 envoy.yaml:42 的 JSON 后 &lt;code&gt;docker compose restart envoy&lt;/code&gt; 即生效，不用重编 wasm——&lt;strong&gt;配置和代码是两条独立的发布链路&lt;/strong&gt;。&lt;/p&gt;&#10;&#10; &lt;/blockquote&gt;&#10;&lt;h2 id="8-设计脉络总结"&gt;&lt;a href="#8-%e8%ae%be%e8%ae%a1%e8%84%89%e7%bb%9c%e6%80%bb%e7%bb%93" class="header-anchor"&gt;&lt;/a&gt;8. 设计脉络总结&#10;&lt;/h2&gt;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt" id="hl-15-1"&gt;&lt;a class="lnlinks" href="#hl-15-1"&gt;1&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-15-2"&gt;&lt;a class="lnlinks" href="#hl-15-2"&gt;2&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-15-3"&gt;&lt;a class="lnlinks" href="#hl-15-3"&gt;3&lt;/a&gt;&#10;&lt;/span&gt;&lt;span class="lnt" id="hl-15-4"&gt;&lt;a class="lnlinks" href="#hl-15-4"&gt;4&lt;/a&gt;&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;加载期 init() → SetCtx 挂号（name 对应 envoy.yaml 的 filter 名）&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;配置期 envoy.yaml 的 configuration JSON → parseGlobalConfig 校验+建索引&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; → _rules_ 每条 → parseOverrideRuleConfig 叠加 allow&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;请求期 取 token（keys 逐个试）→ 数量检查 → 查 map 认证 → 注入 X-Mse-Consumer → allow 授权 → 放行/401/403&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;一句话：&lt;strong&gt;配置期做好校验和索引（慢一次），请求期只做取 token、查 map、比名单（快万次）&lt;/strong&gt;；三态 &lt;code&gt;global_auth&lt;/code&gt; 是唯一绕的地方，记住&amp;quot;nil = 未设置，靠 ruleSet 猜意图&amp;quot;。&lt;/p&gt;&#10;&lt;h2 id="9-已知小问题移植自官方未改动"&gt;&lt;a href="#9-%e5%b7%b2%e7%9f%a5%e5%b0%8f%e9%97%ae%e9%a2%98%e7%a7%bb%e6%a4%8d%e8%87%aa%e5%ae%98%e6%96%b9%e6%9c%aa%e6%94%b9%e5%8a%a8" class="header-anchor"&gt;&lt;/a&gt;9. 已知小问题（移植自官方，未改动）&#10;&lt;/h2&gt;&lt;ul&gt;&#10;&lt;li&gt;&lt;code&gt;in_header&lt;/code&gt;/&lt;code&gt;in_query&lt;/code&gt; 是 &lt;code&gt;else if&lt;/code&gt; 关系，同开时 query 不生效（main.go:268）；&lt;/li&gt;&#10;&lt;li&gt;&lt;code&gt;url.Parse&lt;/code&gt; 错误被忽略（main.go:270）；&lt;/li&gt;&#10;&lt;li&gt;凭证以 &lt;code&gt;%q&lt;/code&gt; 打进日志（main.go:290），有轻微泄露风险；&lt;/li&gt;&#10;&lt;li&gt;落后于上游：官方已支持 consumer 多凭证 &lt;code&gt;credentials&lt;/code&gt; 字段，此版本只有单 &lt;code&gt;credential&lt;/code&gt;（main.go:57）；&lt;/li&gt;&#10;&lt;li&gt;&lt;code&gt;keyauth.yaml&lt;/code&gt; 示例镜像指向个人仓库 &lt;code&gt;docker.io/dongjiang1989/keyauth:1.0.0&lt;/code&gt;，实际部署需替换。&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h2 id="附测试怎么验证这些main_testgo580-行"&gt;&lt;a href="#%e9%99%84%e6%b5%8b%e8%af%95%e6%80%8e%e4%b9%88%e9%aa%8c%e8%af%81%e8%bf%99%e4%ba%9bmain_testgo580-%e8%a1%8c" class="header-anchor"&gt;&lt;/a&gt;附：测试怎么验证这些（main_test.go，580 行）&#10;&lt;/h2&gt;&lt;ul&gt;&#10;&lt;li&gt;&lt;code&gt;TestParseGlobalConfig&lt;/code&gt;（:240）：喂残缺 JSON，验证校验报错；&lt;/li&gt;&#10;&lt;li&gt;&lt;code&gt;TestParseRuleConfig&lt;/code&gt;（:361）：验证 allow 解析与 &lt;code&gt;*config = global&lt;/code&gt; 继承；&lt;/li&gt;&#10;&lt;li&gt;&lt;code&gt;TestOnHTTPRequestHeaders&lt;/code&gt;（:388）：用 wasm-go &lt;code&gt;pkg/test&lt;/code&gt; 的 mock host 模拟请求头，断言 action 与发出的响应状态码——§7 的剧本①②③④都有对应断言。&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;单测（mock host，进程内跑）和 demo（真实 Envoy，容器里跑）是两种互补的验证层：前者快、覆盖分支；后者验证真实 ABI、真实配置链路和实际 HTTP 行为。&lt;/p&gt;&#10;</description></item></channel></rss>