使用 wolf-rbac 插件为 Apache APISIX 接入基于 wolf 的角色访问控制(RBAC)
【免费下载链接】apisixThe Cloud-Native API Gateway项目地址: https://gitcode.com/GitHub_Trending/ap/apisix
wolf-rbac是 Apache APISIX 内置的认证类插件,它将开源权限系统 wolf 的基于角色的访问控制(RBAC)能力以网关插件的形式接入 Route 与 Service,并与 Consumer 对象深度绑定,实现"先登录换令牌、再按资源+动作鉴权"的完整流程。读完本文,你将掌握 wolf-rbac 的全部配置属性、内置 API 端点、与 public-api 配合的暴露方式、四种令牌传递方式,以及从源码层面理解其鉴权调用链,能够独立完成从部署 wolf 到网关鉴权上线的全链路接入。
一、插件简介:把 RBAC 决策交给 wolf,把执行放在网关
wolf-rbac插件将 role-based access control(基于角色的访问控制) 系统 wolf 与 APISIX 打通:wolf 负责用户的认证(登录、改密)与授权(权限、资源、角色管理),APISIX 网关负责在请求进入时校验令牌、向 wolf 发起访问控制查询,并把用户身份透传给后端。
从源码结构看,插件在 apisix/plugins/wolf-rbac.lua 中定义为:
local _M = { version = 0.1, priority = 2555, type = 'auth', name = plugin_name, schema = schema, }type = 'auth':属于认证类插件,可与 Consumer 绑定使用;priority = 2555:优先级较高,确保在路由匹配后、其他业务插件之前执行鉴权逻辑;- 鉴权核心逻辑在
rewrite阶段完成,即请求在进入上游之前就被拦截校验。
该插件可与 Consumer 配合使用:每个 Consumer 上配置一个appid,网关通过令牌中的appid定位对应的 Consumer 与 wolf 服务地址,从而支持多个应用(多租户)共用一套 APISIX 网关、各自对接不同的 wolf 配置。
二、配置属性详解
wolf-rbac的配置属性如下:
| 名称 | 类型 | 必填 | 默认值 | 描述 |
|---|---|---|---|---|
| server | string | 否 | "http://127.0.0.1:12180" | wolf 服务的地址 |
| appid | string | 否 | "unset" | 在 wolf 控制台中添加的应用 ID(App id),该字段支持通过 APISIX Secret 资源保存到密钥管理器中 |
| header_prefix | string | 否 | "X-" | 自定义 HTTP 头前缀。认证成功后,会向后端请求头与前端响应头中各添加三个头:X-UserId、X-Username、X-Nickname |
属性细节与源码印证
以上默认值可以直接在源码的 schema 定义中看到(apisix/plugins/wolf-rbac.lua):
local schema = { type = "object", properties = { appid = { type = "string", default = "unset" }, server = { type = "string", default = "http://127.0.0.1:12180" }, header_prefix = { type = "string", default = "X-" }, } }在测试用例 t/plugin/wolf-rbac.t 的 TEST 1 中,不传任何配置时check_schema会得到默认值{"appid":"unset","header_prefix":"X-","server":"http:\/\/127\.0\.0\.1:12180"};TEST 2 则验证了类型校验——appid传入数字会报错property "appid" validation failed: wrong type: expected string, got number。
需要注意:server的值在运行时决定与 wolf 通信的目标地址,因此同一个 APISIX 集群可以为不同 Consumer 配置不同的 wolf 服务端;而appid必须是在 wolf 控制台中真实存在并已完成资源授权的应用 ID,否则登录与鉴权都会失败。
header_prefix的可配置性意味着在多应用共用网关时,可以避免不同应用的认证头互相冲突(例如配置为App-后,透传头变为App-UserId等)。
三、插件内置 API 端点
启用该插件后,会注册以下三个内部 API 端点(源码见 apisix/plugins/wolf-rbac.lua 的_M.api()):
| 端点 | HTTP 方法 | 功能 |
|---|---|---|
/apisix/plugin/wolf-rbac/login | POST | 用户登录,返回rbac_token与用户信息 |
/apisix/plugin/wolf-rbac/change_pwd | PUT | 修改用户密码 |
/apisix/plugin/wolf-rbac/user_info | GET | 获取当前用户信息 |
:::note 与 jwt-auth 等插件一致,这些端点默认不对外暴露,需要通过 public-api 插件配合一条 Route 将其公开。 :::
public-api插件(源码见 apisix/plugins/public-api.lua)的作用是把插件内部注册的 API 通过通用 HTTP 路由暴露出去:在 Route 上启用public-api后,其access阶段会调用router.api.match(ctx)完成内部 API 路由匹配,从而把请求转发给 wolf-rbac 的内部 handler。
四、前置条件:先准备好 wolf 服务端
使用本插件前,必须先在环境中安装并启动 wolf,然后通过 wolf-console 完成以下配置:
- 安装并启动 wolf(可采用其官方仓库提供的 Docker 快速启动方式);
- 在 wolf-console 中依次添加:
application(应用)、admin(管理员)、normal user(普通用户)、permission(权限)、resource(资源); - 为上述用户完成授权(user authorize),使该用户对相关资源拥有对应操作的权限。
这些配置直接决定了后续登录、访问控制查询的结果:登录依赖 wolf 中存在的用户与密码,鉴权依赖 wolf 中"资源 + 动作 + 用户角色"的授权关系。
五、启用插件
wolf-rbac的配置必须挂在 Consumer 上(在 Route 上仅写"wolf-rbac": {}空配置即可),这样网关才能通过令牌中的appid找到对应的 wolf 服务地址与 Consumer 上下文。
5.1 获取 admin_key
Admin API 默认开启了认证(对应 conf/config.yaml.example 中的admin_key_required: true与admin_key配置)。可以从config.yaml中取出 admin key 并保存为环境变量:
admin_key=$(yq '.deployment.admin.admin_key[0].key' conf/config.yaml | sed 's/"//g')5.2 在 Consumer 上配置插件
curl http://127.0.0.1:9180/apisix/admin/consumers -H "X-API-KEY: $admin_key" -X PUT -d ' { "username":"wolf_rbac", "plugins":{ "wolf-rbac":{ "server":"http://127.0.0.1:12180", "appid":"restful" } }, "desc":"wolf-rbac" }':::note 配置中的appid必须已经在 wolf 中存在,否则后续登录/鉴权会失败。 :::
从源码实现看,网关在鉴权时通过consumer.plugin(plugin_name)获取全局的 wolf-rbac Consumer 配置,再用consumer.consumers_kv(plugin_name, consumer_conf, "appid")以appid为键建立索引(见 apisix/plugins/wolf-rbac.lua),因此一个 Consumer 对应一个 appid、一个 wolf 服务地址是设计上的基本单元。
5.3 将插件绑定到 Route 或 Service
curl http://127.0.0.1:9180/apisix/admin/routes/1 -H "X-API-KEY: $admin_key" -X PUT -d ' { "methods": ["GET"], "uri": "/*", "plugins": { "wolf-rbac": {} }, "upstream": { "type": "roundrobin", "nodes": { "www.baidu.com:80": 1 } } }'也可以使用 APISIX Dashboard 通过 Web 界面完成上述操作(添加 Consumer、启用 wolf-rbac 插件)。
六、使用示例:从登录到访问受保护资源
6.1 用 public-api 暴露登录端点
wolf-rbac 的内部 API 默认不对外,需要先创建一条启用public-api插件的 Route:
curl http://127.0.0.1:9180/apisix/admin/routes/wal -H "X-API-KEY: $admin_key" -X PUT -d ' { "uri": "/apisix/plugin/wolf-rbac/login", "plugins": { "public-api": {} } }'change_pwd与user_info两个端点同样需要各自创建一条 public-api Route 才能对外访问。
6.2 登录并获取 rbac_token
使用 JSON 请求体登录:
curl http://127.0.0.1:9080/apisix/plugin/wolf-rbac/login -i \ -H "Content-Type: application/json" \ -d '{"appid": "restful", "username":"test", "password":"user-password", "authType":1}'响应示例:
HTTP/1.1 200 OK Date: Wed, 24 Jul 2019 10:33:31 GMT Content-Type: text/plain Transfer-Encoding: chunked Connection: keep-alive Server: APISIX web server {"rbac_token":"V1#restful#eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6NzQ5LCJ1c2VybmFtZSI6InRlc3QiLCJtYW5hZ2VyIjoiIiwiYXBwaWQiOiJyZXN0ZnVsIiwiaWF0IjoxNTc5NDQ5ODQxLCJleHAiOjE1ODAwNTQ2NDF9.n2-830zbhrEh6OAxn4K_yYtg5pqfmjpZAjoQXgtcuts","user_info":{"nickname":"test","username":"test","id":"749"}}:::note 请求中的appid、username、password必须在 wolf 系统中预先配置;authType为认证类型——1 表示密码认证(默认),2 表示 LDAP 认证(v0.5.0+)。 :::
也可以使用x-www-form-urlencoded表单方式提交(源码get_args会先判断Content-Type是否为application/json,否则走core.request.get_post_args解析表单,见 apisix/plugins/wolf-rbac.lua):
curl http://127.0.0.1:9080/apisix/plugin/wolf-rbac/login -i \ -H "Content-Type: application/x-www-form-urlencoded" \ -d 'appid=restful&username=test&password=user-password'登录成功后,网关会把 wolf 返回的 token 包装成统一格式的rbac_token。从源码看其格式为V1#appid#wolf_token(见create_rbac_token,apisix/plugins/wolf-rbac.lua),wolf_token本体是 wolf 签发的 JWT。
6.3 携带令牌访问受保护路由
wolf-rbac支持四种令牌传递方式,网关按固定顺序从请求中提取令牌(源码fetch_rbac_token,见 apisix/plugins/wolf-rbac.lua):
- URL 查询参数
rbac_token(ctx.var.arg_rbac_token,会自动进行 URL 解码); Authorization请求头;x-rbac-token请求头;- Cookie
x-rbac-token。
下面逐一验证:
- 不带令牌:返回
401 Unauthorized
curl http://127.0.0.1:9080/ -H"Host: www.baidu.com" -iHTTP/1.1 401 Unauthorized ... {"message":"Missing rbac token in request"}- 放在
Authorization头中:
curl http://127.0.0.1:9080/ -H"Host: www.baidu.com" \ -H 'Authorization: V1#restful#eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6NzQ5LCJ1c2VybmFtZSI6InRlc3QiLCJtYW5hZ2VyIjoiIiwiYXBwaWQiOiJyZXN0ZnVsIiwiaWF0IjoxNTc5NDQ5ODQxLCJleHAiOjE1ODAwNTQ2NDF9.n2-830zbhrEh6OAxn4K_yYtg5pqfmjpZAjoQXgtcuts' -iHTTP/1.1 200 OK <!DOCTYPE html>- 放在
x-rbac-token头中:
curl http://127.0.0.1:9080/ -H"Host: www.baidu.com" \ -H 'x-rbac-token: V1#restful#eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6NzQ5LCJ1c2VybmFtZSI6InRlc3QiLCJtYW5hZ2VyIjoiIiwiYXBwaWQiOiJyZXN0ZnVsIiwiaWF0IjoxNTc5NDQ5ODQxLCJleHAiOjE1ODAwNTQ2NDF9.n2-830zbhrEh6OAxn4K_yYtg5pqfmjpZAjoQXgtcuts' -iHTTP/1.1 200 OK <!DOCTYPE html>- 放在 URL 查询参数中:
curl 'http://127.0.0.1:9080?rbac_token=V1%23restful%23eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6NzQ5LCJ1c2VybmFtZSI6InRlc3QiLCJtYW5hZ2VyIjoiIiwiYXBwaWQiOiJyZXN0ZnVsIiwiaWF0IjoxNTc5NDQ5ODQxLCJleHAiOjE1ODAwNTQ2NDF9.n2-830zbhrEh6OAxn4K_yYtg5pqfmjpZAjoQXgtcuts' -H"Host: www.baidu.com" -iHTTP/1.1 200 OK <!DOCTYPE html>- 放在 Cookie 中:
curl http://127.0.0.1:9080 -H"Host: www.baidu.com" \ --cookie x-rbac-token=V1#restful#eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6NzQ5LCJ1c2VybmFtZSI6InRlc3QiLCJtYW5hZ2VyIjoiIiwiYXBwaWQiOiJyZXN0ZnVsIiwiaWF0IjoxNTc5NDQ5ODQxLCJleHAiOjE1ODAwNTQ2NDF9.n2-830zbhrEh6OAxn4K_yYtg5pqfmjpZAjoQXgtcuts -iHTTP/1.1 200 OK <!DOCTYPE html>上述四种方式在测试用例 t/plugin/wolf-rbac.t 的 TEST 17~20 中均有覆盖,且都断言了响应头中透传的X-UserId: 100、X-Username: admin、X-Nickname: administrator。
6.4 获取用户信息
curl http://127.0.0.1:9080/apisix/plugin/wolf-rbac/user_info \ --cookie x-rbac-token=V1#restful#eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6NzQ5LCJ1c2VybmFtZSI6InRlc3QiLCJtYW5hZ2VyIjoiIiwiYXBwaWQiOiJyZXN0ZnVsIiwiaWF0IjoxNTc5NDQ5ODQxLCJleHAiOjE1ODAwNTQ2NDF9.n2-830zbhrEh6OAxn4K_yYtg5pqfmjpZAjoQXgtcuts -iHTTP/1.1 200 OK { "user_info":{ "nickname":"test", "lastLogin":1582816780, "id":749, "username":"test", "appIDs":["restful"], "manager":"none", "permissions":{"USER_LIST":true}, "profile":null, "roles":{}, "createTime":1578820506, "email":"" } }6.5 修改用户密码
curl http://127.0.0.1:9080/apisix/plugin/wolf-rbac/change_pwd \ -H "Content-Type: application/json" \ --cookie x-rbac-token=V1#restful#eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6NzQ5LCJ1c2VybmFtZSI6InRlc3QiLCJtYW5hZ2VyIjoiIiwiYXBwaWQiOiJyZXN0ZnVsIiwiaWF0IjoxNTc5NDQ5ODQxLCJleHAiOjE1ODAwNTQ2NDF9.n2-830zbhrEh6OAxn4K_yYtg5pqfmjpZAjoQXgtcuts -i \ -X PUT -d '{"oldPassword": "old password", "newPassword": "new password"}'HTTP/1.1 200 OK {"message":"success to change password"}七、鉴权流程:源码级调用链解读
在rewrite阶段,插件对每个进入受保护路由的请求执行如下步骤(核心逻辑见 apisix/plugins/wolf-rbac.lua 的_M.rewrite):
- 提取令牌:按"查询参数 → Authorization 头 → x-rbac-token 头 → Cookie"的顺序调用
fetch_rbac_token;取不到令牌直接返回401 {"message":"Missing rbac token in request"}(对应测试 TEST 13)。 - 解析令牌:
parse_rbac_token按#分割为三段,校验版本号必须是V1、且段数为 3;格式非法返回401 {"message":"invalid rbac token: parse failed"}(对应测试 TEST 14)。 - 定位 Consumer:以令牌中的
appid为键查找 Consumer;未找到返回401 {"message":"Invalid appid in rbac token"}(对应测试 TEST 15,日志会输出consumer [invalid-appid] not found)。 - 向 wolf 发起访问控制查询:调用
check_url_permission,请求{server}/wolf/rbac/access_check,携带x-rbac-token(wolf 原始 JWT)、appID、resName(当前请求 URI)、action(当前 HTTP 方法)、clientIP等参数;该请求设置了 10 秒超时,并在遇到 5xx 状态码时最多重试 3 次、每次间隔 100ms(见 apisix/plugins/wolf-rbac.lua)。 - 处理鉴权结果:wolf 返回非 200 时,按原因返回对应错误(如
ERR_ACCESS_DENIED对应 403、ERR_TOKEN_INVALID对应 401、wolf 服务 5xx 对应 500);校验通过后调用consumer.attach_consumer将 Consumer 上下文绑定到请求,供后续插件使用。 - 身份透传:鉴权通过后,把
userInfo中的id、username、nickname分别以{header_prefix}UserId、{header_prefix}Username、{header_prefix}Nickname写入后端请求头与前端响应头(nickname会做ngx.escape_uri编码),默认即X-UserId、X-Username、X-Nickname。
测试用例 TEST 29、TEST 30 还覆盖了异常路径:wolf 返回 500 时网关返回{"message":"request to wolf-server failed, status:500"},令牌过期时返回ERR_TOKEN_INVALID。TEST 36~37 则验证了 wolf-rbac 与 Consumer 上其他插件(如 echo)的合并执行,证明认证通过后 Consumer 级插件配置会正常生效。
八、进阶:使用 Secret 管理 appid
appid字段支持 APISIX Secret 引用,避免敏感信息以明文出现在 Consumer 配置中。例如先把密钥存入 HashiCorp Vault:
VAULT_TOKEN='root' VAULT_ADDR='http://0.0.0.0:8200' vault kv put kv/apisix/wolf_rbac_unit_test appid=wolf-rbac-app再在 Consumer 配置中通过$secret://引用:
curl http://127.0.0.1:9180/apisix/admin/consumers -H "X-API-KEY: $admin_key" -X PUT -d ' { "username": "wolf_rbac_unit_test", "plugins": { "wolf-rbac": { "appid": "$secret://vault/test1/wolf_rbac_unit_test/appid", "server": "http://127.0.0.1:1982" } } }'其中test1是预先通过 Admin API(/apisix/admin/secrets/vault/test1)创建的 Vault 资源 ID。测试用例 t/plugin/wolf-rbac.t 的 TEST 31~35 完整覆盖了"Vault 存密钥 → Secret 引用 → 登录成功"的链路,并且也验证了 Vault token 本身可以用$ENV://VAULT_TOKEN环境变量引用。
九、删除插件
要移除wolf-rbac插件,只需把对应配置从 Route/Service/Consumer 的 JSON 配置中删除。APISIX 会自动热加载新配置,无需重启即可生效:
curl http://127.0.0.1:9180/apisix/admin/routes/1 -H "X-API-KEY: $admin_key" -X PUT -d ' { "methods": ["GET"], "uri": "/*", "plugins": { }, "upstream": { "type": "roundrobin", "nodes": { "www.baidu.com:80": 1 } } }'十、小结与实践建议
wolf-rbac为 APISIX 提供了与 wolf 深度集成的 RBAC 能力,其核心设计可以概括为三点:
- 配置集中:
server与appid定义在 Consumer 上,一个 Consumer 对应一个 wolf 应用,天然支持多应用多租户; - 执行前置:鉴权在
rewrite阶段完成,未认证请求在进入上游前即被拦截,令牌支持请求头、Cookie、查询参数多种携带方式,适配 Web 与移动端场景; - 身份可透传:认证通过后自动向后端与前端注入
X-UserId/X-Username/X-Nickname三个头,后端无需再解析令牌即可获得用户身份。
实践中建议将appid等敏感字段通过 APISIX Secret 管理;登录、改密、用户信息三个端点务必通过 public-api 明确暴露(必要时可再叠加其他访问限制插件),避免内部 API 被随意调用。完整的端到端行为可以参照测试用例 t/plugin/wolf-rbac.t 验证,插件实现源码位于 apisix/plugins/wolf-rbac.lua。
【免费下载链接】apisixThe Cloud-Native API Gateway项目地址: https://gitcode.com/GitHub_Trending/ap/apisix
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考