鉴权与 consumer
"谁能访问我的 API"是网关核心能力。今天串起鉴权全流程:consumer 数据怎么加载、key-auth 插件怎么在 rewrite 阶段识别身份、attach_consumer 怎么把 consumer 挂到 ctx 上触发 Day 04 的二次合并。
鉴权的位置
鉴权插件(key-auth/jwt-auth/basic-auth…)都实现 rewrite 方法(priority 很高,如 key-auth),在 Day 04 的第一轮 rewrite 就跑——早早确认身份。
consumer 的 init_worker
consumer.lua:300 的 init_worker watch etcd 的 /consumers(Day 07),把所有 consumer 同步进内存。consumers_kv(:263)按"插件名 + key 字段"建索引,方便按 key 快速查。
(plugin_name, key) 组织成查找表——比如 key-auth 的所有 consumer 按它们的 key 值建索引。这样请求带一个 key 来,能 O(1) 查到对应 consumer。存入前还会 check_consumer(:284)用 schema 校验(Day 09)。key-auth rewrite
plugins/key-auth.lua:104-122:
function _M.rewrite(conf, ctx)
local consumer, consumer_conf, err = find_consumer(ctx, conf) -- 按请求里的 key 查
if not consumer then
if not conf.anonymous_consumer then
return 401, { message = err} -- 没找到且不允许匿名 → 拦截 401
end
-- 允许匿名:降级到匿名 consumer
consumer, consumer_conf, err = consumer_mod.get_anonymous_consumer(conf.anonymous_consumer)
if not consumer then return 401, { message = "Invalid user authorization"} end
end
consumer_mod.attach_consumer(ctx, consumer, consumer_conf) -- 识别成功,挂到 ctx
end
find_consumer
consumer.lua:271-282:
function _M.find_consumer(plugin_name, key, key_value)
local consumer_conf = _M.plugin(plugin_name) -- 该鉴权插件的所有 consumer
if not consumer_conf then return nil, nil, "Missing related consumer" end
local consumers = _M.consumers_kv(plugin_name, consumer_conf, key) -- 按 key 建的索引
local consumer = consumers[key_value] -- 用请求带的 key 值查
return consumer, consumer_conf
end
key_value(请求带来的实际 key)直接取。不查 etcd、不查数据库,内存 O(1)。这就是为什么 consumer 要在 init_worker 时建好索引(L02)。查到就是认证成功。alice,key-auth 的 key = "abc123"。请求带 apikey: abc123 来 → find_consumer 用 key_value="abc123" 在索引表里一查 → 命中 alice → ctx.consumer = alice。换成
apikey: wrong → 表里查不到 → 返回 401 {"message":"Invalid API key..."}。👶 小白:每来一个请求都要去 etcd 查一遍会员卡吗?那不慢死?
👨🏫 老师:不。所有 consumer 在 worker 启动时(L02 init_worker)就从 etcd 同步进内存、按 key 建好了查找表(像门卫开工前先把会员名册背进脑子)。请求来了纯内存 O(1) 查表,不碰 etcd。etcd 那边有人改了 consumer,watch(Day 07)会自动更新这张内存表。
attach_consumer
consumer.lua:213 的 attach_consumer(ctx, consumer, conf):把识别出的 consumer 挂到 ctx.consumer(和 consumer_name/group_id 等)。
if api_ctx.consumer then ...——就是这里 attach 的。一旦挂上 consumer,就触发"合并 consumer 专属配置 + 补跑 rewrite_in_consumer"(Day 12-13)。所以鉴权插件的职责就是"识别身份并 attach",之后的"按身份定制"由框架自动接管。attach 后,后续插件(限流等)和日志都能拿到 ctx.consumer 知道"这是谁"。匿名 consumer
consumer.lua:335 的 get_anonymous_consumer:为"没带凭据"的请求提供一个预设的匿名身份。
anonymous_consumer 后,没凭据的请求不返回 401,而是当成匿名 consumer——它可以有自己的限流等配置(比如匿名 10 QPS、登录 100 QPS)。这比"要么全拒要么全放"灵活。本质还是走 consumer 那套(匿名也是个 consumer),复用同一机制。其他鉴权插件
APISIX 的鉴权插件家族(都在 plugins/,都实现 rewrite + attach_consumer 套路):
- key-auth:API Key(最简单)
- jwt-auth:JWT token 验证
- basic-auth:HTTP Basic 用户名密码
- hmac-auth:HMAC 签名
- ldap-auth / openid-connect:对接 LDAP / OIDC
attach_consumer——后续流程完全一致。回想上一站 Higress Console 只支持 key-auth(Day 13),APISIX 的鉴权生态丰富得多。这体现了"内核统一(consumer 机制)+ 插件多样(各种验证方式)"的插件化威力。今日小结 + 动手
🧠 今天你应该能回答
- 为什么鉴权在 rewrite 且 priority 高?
- find_consumer 怎么内存 O(1) 查?consumer 何时建索引?
- attach_consumer 触发了 Day 04 的什么连锁反应?
- 匿名 consumer 的用途?多种鉴权插件的共性与差异?
✋ 动手
cd /Users/bitmart/work/codes/github/apisix
sed -n '104,122p' apisix/plugins/key-auth.lua
sed -n '213,300p' apisix/consumer.lua
ls apisix/plugins/ | grep -E "auth"