Apache APISIX wolf-rbac 插件实战:基于 wolf 的 RBAC 身份认证与权限控制

发布时间:2026/9/15 17:54:57
Apache APISIX wolf-rbac 插件实战:基于 wolf 的 RBAC 身份认证与权限控制 Apache APISIX wolf-rbac 插件实战基于 wolf 的 RBAC 身份认证与权限控制【免费下载链接】apisixThe Cloud-Native API Gateway项目地址: https://gitcode.com/GitHub_Trending/ap/apisix导读wolf-rbac是 Apache APISIX 中一个基于 Role-Based Access ControlRBAC基于角色的访问控制 为主体结合 插件源码 与 单元测试 展开带你完整掌握插件的属性与接口、Consumer 与 Route 的配置方式、如何借助public-api插件暴露登录/改密/用户信息接口以及 rbac_token 的多种携带方式与底层鉴权原理。描述为网关接入 wolf RBAC 鉴权wolf-rbac插件为 APISIX 的 Route 或 Service 提供接入 wolf 权限系统的能力。它是一个认证auth类型插件从源码看其优先级为2555类型为auth见 apisix/plugins/wolf-rbac.lua意味着它会在路由匹配之后、其他业务插件执行之前完成身份与权限校验。该插件必须与 Consumer 配合使用插件通过 Consumer 上绑定的appid关联到具体的应用再以该应用的身份向 wolf-server 发起权限校验。从 Consumer 术语文档 可以看到wolf-rbac与basic-auth、hmac-auth、jwt-auth、key-auth、ldap-auth并列是 APISIX 支持与 Consumer 配置搭配的认证插件之一。其整体工作流程可以概括为用户在 wolf 系统中维护应用appid、用户、角色、权限、资源等数据APISIX 在 Consumer 上绑定wolf-rbac插件并声明appid客户端携带rbac_token请求受保护 Route插件解析 token向 wolf-server 的/wolf/rbac/access_check接口查询该用户对此 URL 的该 HTTP 方法是否有权限校验通过则放行并注入用户信息响应头失败则返回 401/403 等状态码。属性说明wolf-rbac插件的配置属性如下表所示这些属性与源码中的 schema 定义 一一对应名称类型必选项默认值描述serverstring否http://127.0.0.1:12180wolf-server的服务地址。appidstring否unset在wolf-console中已经添加的应用 ID。该字段支持使用 APISIX Secret 资源将值保存在 Secret Manager 中。header_prefixstring否X-自定义 HTTP 头的前缀。鉴权成功后插件会在请求头传给后端与响应头传给前端中额外添加 3 个 headerX-UserId、X-Username、X-Nickname。参数细节与源码印证server即 wolf-server 的访问地址默认指向本机12180端口。从源码看check_schema 会通过core.utils.check_https检查该地址是否为https开头若是 HTTPS 地址则要求 APISIX 已加载相应的 SSL 证书否则返回 schema 校验错误。appid对应 wolf 控制台中注册的应用 ID是关联 wolf 侧权限数据的核心字段。它的默认值是字符串unset在单元测试 TEST 1: sanity 中可以看到当配置为空对象时schema 校验会填充出{appid:unset,header_prefix:X-,server:http://127.0.0.1:12180}的完整默认配置。appid支持 Secret 引用appid可写成$secret://vault/test1/wolf_rbac_unit_test/appid这样的引用形式将敏感值托管到 Vault 等 Secret Manager 中。这一点在 测试用例 TEST 31-35 中有完整的验证先创建 vault secret 资源再在 Consumer 的wolf-rbac配置中使用$secret://引用登录测试依然通过。header_prefix控制注入头的前缀。例如设置为X-时注入X-UserId、X-Username、X-Nickname若设置为空字符串则注入不带前缀的UserId、Username、Nickname。插件接口启用该插件后它会在 APISIX 内部注册以下 3 个接口对应源码 _M.api() 的注册表接口方法用途/apisix/plugin/wolf-rbac/loginPOST用户名密码登录换取rbac_token/apisix/plugin/wolf-rbac/change_pwdPUT修改当前用户密码/apisix/plugin/wolf-rbac/user_infoGET获取当前用户信息:::note 注意以上接口默认不对外暴露需要通过 public-api 插件创建路由后才能从外部访问。这与public-api插件的设计一致自定义插件注册的公共 API 端点默认隐藏需手动配置路由启用。:::从源码实现看这三个接口的 handler 都遵循同样的模式wolf_rbac_login源码解析请求体中的appid、username、password、authType等参数从 Consumer 配置中取出server地址调用 wolf-server 的/wolf/rbac/login.rest接口完成认证然后将 wolf 返回的 token 拼装成V1#appid#wolf_token格式的rbac_token返回给客户端。wolf_rbac_change_pwd源码先从当前请求中解析rbac_token缺 token 返回 401再携带x-rbac-token请求头调用 wolf-server 的/wolf/rbac/change_pwd接口。wolf_rbac_user_info源码同样先校验 token再调用 wolf-server 的/wolf/rbac/user_info接口返回用户详情。前提条件安装并初始化 wolf在使用插件之前你必须要安装并启动 wolf参考 wolf 官方提供的 Docker 快速启动方式完成部署。初始化权限数据在 wolf-console 控制台中添加application应用、admin管理员、regular user普通用户、permission权限、resource资源等数据并将用户授权到对应应用。注意wolf-console 中维护的数据是鉴权的事实来源。APISIX 只负责把请求转发给 wolf-server 进行校验插件本身不存储用户与权限信息。启用插件第一步创建 Consumer 并绑定插件首先创建一个 Consumer并在其plugins中配置wolf-rbac:::note可以这样从 conf/config.yaml 中获取admin_key并存入环境变量admin_key定义于deployment.admin.admin_key配置项admin_key$(yq .deployment.admin.admin_key[0].key conf/config.yaml | sed s///g):::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这里是restful必须是已经在 wolf 控制台中存在的应用 ID否则后续登录与鉴权都会失败。:::为什么appid要放在 Consumer 上从源码的鉴权流程rewrite 函数可以看出插件在收到请求后会先解析 token 中的appid然后通过consumer.consumers_kv(plugin_name, consumer_conf, appid)按appid索引找到对应的 Consumer 配置进而取得该应用的server地址。也就是说一个 appid 对应一个 Consumertoken 中的 appid 决定了使用哪个 Consumer 的 wolf-server 配置。如果 token 里的 appid 找不到对应的 Consumer插件会直接返回 401Invalid appid in rbac token对应测试 TEST 15。第二步为 Route 或 Service 添加插件然后将wolf-rbac插件挂载到需要保护的 Route 上。下面的示例让 Route 1 匹配所有 GET 请求并启用插件配置项留空即可全部使用默认值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 } } }同样地插件也可以挂载到 Service 上这样该 Service 下的所有 Route 都会继承鉴权配置。此外你还可以通过 APISIX Dashboard 的 Web 界面完成上述操作。第三步暴露插件的三个接口public-api由于/apisix/plugin/wolf-rbac/login等接口默认不对外暴露需要创建路由并启用 public-api 插件来将它们开放出来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两个 API 分别配置路由在单元测试 TEST 3: setup public API route 中正是依次为login、user_info、change_pwd创建了三条 public-api 路由。测试插件登录并获取 rbac_token在配置好 public-api 路由后就可以通过网关默认9080端口调用登录接口了。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}返回示例200 OK响应体包含rbac_token与user_infoHTTP/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}}x-www-form-urlencoded 格式登录curl http://127.0.0.1:9080/apisix/plugin/wolf-rbac/login -i \ -H Content-Type: application/x-www-form-urlencoded \ -d appidrestfulusernametestpassworduser-password:::note上述示例中appid、username和password必须为 wolf 系统中真实存在的authType为认证类型1为密码认证默认2为 LDAP 认证。wolf从 0.5.0 版本开始支持 LDAP 认证。:::rbac_token 的格式从源码 create_rbac_token 可以看到token 由三段以#分隔的字段组成版本号(V1)#appid#wolf_token。其中第三段是 wolf 返回的原始 token实际为 JWT包含id、username、appid、iat、exp等声明。解析时parse_rbac_token要求恰好拆成 3 段且版本号必须是V1否则返回invalid rbac token: version。登录失败场景单元测试 TEST 6-11 覆盖了登录接口的各种异常路径缺少appid返回 400appid is missing、appid 不存在返回 400appid not found、用户名/密码缺失或错误、用户不存在时网关会转发 wolf-server 返回的错误码如ERR_USERNAME_MISSING、ERR_PASSWORD_ERROR、ERR_USER_NOT_FOUND并给出request to wolf-server failed!的响应。使用受保护的 Route现在开始测试 Route 的鉴权效果。以下场景均以示例中的rbac_token为准。1. 缺少 tokencurl http://127.0.0.1:9080/ -HHost: www.baidu.com -iHTTP/1.1 401 Unauthorized ... {message:Missing rbac token in request}2. token 放到请求头Authorization中curl http://127.0.0.1:9080/ -HHost: www.baidu.com \ -H Authorization: V1#restful#eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6NzQ5LCJ1c2VybmFtZSI6InRlc3QiLCJtYW5hZ2VyIjoiIiwiYXBwaWQiOiJyZXN0ZnVsIiwiaWF0IjoxNTc5NDQ5ODQxLCJleHAiOjE1ODAwNTQ2NDF9.n2-830zbhrEh6OAxn4K_yYtg5pqfmjpZAjoQXgtcuts -iHTTP/1.1 200 OK !DOCTYPE html3. token 放到请求头x-rbac-token中curl http://127.0.0.1:9080/ -HHost: www.baidu.com \ -H x-rbac-token: V1#restful#eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6NzQ5LCJ1c2VybmFtZSI6InRlc3QiLCJtYW5hZ2VyIjoiIiwiYXBwaWQiOiJyZXN0ZnVsIiwiaWF0IjoxNTc5NDQ5ODQxLCJleHAiOjE1ODAwNTQ2NDF9.n2-830zbhrEh6OAxn4K_yYtg5pqfmjpZAjoQXgtcuts -iHTTP/1.1 200 OK !DOCTYPE html4. token 放到请求参数中curl http://127.0.0.1:9080?rbac_tokenV1%23restful%23eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6NzQ5LCJ1c2VybmFtZSI6InRlc3QiLCJtYW5hZ2VyIjoiIiwiYXBwaWQiOiJyZXN0ZnVsIiwiaWF0IjoxNTc5NDQ5ODQxLCJleHAiOjE1ODAwNTQ2NDF9.n2-830zbhrEh6OAxn4K_yYtg5pqfmjpZAjoQXgtcuts -HHost: www.baidu.com -iHTTP/1.1 200 OK !DOCTYPE html5. token 放到cookie中curl http://127.0.0.1:9080 -HHost: www.baidu.com \ --cookie x-rbac-tokenV1#restful#eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6NzQ5LCJ1c2VybmFtZSI6InRlc3QiLCJtYW5hZ2VyIjoiIiwiYXBwaWQiOiJyZXN0ZnVsIiwiaWF0IjoxNTc5NDQ5ODQxLCJleHAiOjE1ODAwNTQ2NDF9.n2-830zbhrEh6OAxn4K_yYtg5pqfmjpZAjoQXgtcuts -iHTTP/1.1 200 OK !DOCTYPE htmltoken 的查找优先级以上 4 种携带方式对应源码 fetch_rbac_token 的取值顺序——依次检查 URL 参数rbac_token注意会先做ngx.unescape_uri解码、请求头Authorization、请求头x-rbac-token、cookie_x-rbac-token。全部取不到时返回 nil进而触发 401。单元测试 TEST 17-20 逐一验证了这 4 种方式且都断言了响应头中注入了X-UserId: 100、X-Username: admin、X-Nickname: administrator。获取用户信息通过user_info接口可以拿到当前 token 对应用户在 wolf 中的完整资料curl http://127.0.0.1:9080/apisix/plugin/wolf-rbac/user_info \ --cookie x-rbac-tokenV1#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: } }更改用户密码通过change_pwd接口修改当前用户的密码需要携带旧密码与新密码同时以 cookie 方式提供 tokencurl http://127.0.0.1:9080/apisix/plugin/wolf-rbac/change_pwd \ -H Content-Type: application/json \ --cookie x-rbac-tokenV1#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}补充说明从源码看change_pwd与user_info接口都要求请求中携带有效 tokenget_wolf_token否则返回 401Missing rbac token in request或invalid rbac token: parse failed对应测试 TEST 21-22。同时请求体既支持 JSON 也支持 x-www-form-urlencoded见 get_args后者借助core.request.get_post_args解析测试 TEST 27-28 验证了 form 方式以及超过 100 个参数场景下的可用性。底层鉴权流程与故障处理源码解析理解插件在rewrite阶段的完整处理链路有助于在生产环境中排查问题。整个流程见 rewrite 函数提取上下文获取当前请求的uri、HTTP 方法request_method、客户端 IP优先取X-Real-IP否则通过core.request.get_ip获取取 token按上文优先级从参数/请求头/cookie 中提取rbac_token缺失则返回 401Missing rbac token in request解析 token校验格式与版本失败返回 401invalid rbac token: parse failed匹配 Consumer用 token 中的appid反查 Consumer 配置失败返回 401Invalid appid in rbac token调用 wolf-server 校验向{server}/wolf/rbac/access_check?appID...resNameuriactionHTTP方法clientIP...发起 GET 请求并在请求头携带x-rbac-token。从 check_url_permission 可以看到该调用带 10 秒超时且对5xx响应最多重试 3 次间隔 0.1 秒以容忍 wolf-server 的瞬时故障注入用户信息头校验成功后将UserId、Username、Nickname按header_prefix拼装同时写入响应头core.response.set_header用于传给前端和请求头core.request.set_header用于传给后端。其中Nickname会经过ngx.escape_uri编码若用户未设置昵称则回退为username处理失败响应res.status非 200 时直接返回 wolf-server 给出的状态码与错误信息。典型场景包括403用户无权限如ERR_ACCESS_DENIED测试 TEST 16401token 过期或无效如ERR_TOKEN_INVALID测试 TEST 30500wolf-server 内部错误或网络不可达如request to wolf-server failed, status:500测试 TEST 29。另外值得注意的是wolf-server 的access_check会基于请求的 URI 与 HTTP 方法判断权限因此同一个 Route 上不同方法的请求可能得到不同的鉴权结果——这正是 RBAC 中资源 动作模型的体现resName即请求的uriaction即 HTTP 方法如GET、POST。删除插件当你需要禁用wolf-rbac插件时只需通过 Admin API 将 Route或 Service、Consumer配置中的plugins清空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 } } }注意删除 Route 上的插件后仅该 Route 不再鉴权。若之前在 Consumer 上也配置了wolf-rbac可根据需要一并删除同时由public-api暴露的login、user_info、change_pwd三个接口路由在不再需要时也应一并清理避免鉴权接口继续对外开放。小结wolf-rbac插件将身份认证 细粒度权限控制从业务代码中剥离出来交给网关统一处理认证由 wolf 完成APISIX 负责 token 解析、Consumer 匹配与权限查询并通过注入的X-UserId/X-Username/X-Nickname头把用户身份透传给上游与前端。结合 Consumer、public-api 与 Secret 能力它可以完整覆盖登录换 token、携带 token 访问受保护资源、查询用户信息、修改密码这一整套基于角色的访问控制场景。若需深入理解实现细节可继续阅读 插件源码 与 单元测试。【免费下载链接】apisixThe Cloud-Native API Gateway项目地址: https://gitcode.com/GitHub_Trending/ap/apisix创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考