Route.Allow 或服务级别的 Allow。
访问层
前三层由内核配置定义。最后一层由服务在注册路由时声明。关于服务如何注册路由、以及声明中哪一部分携带访问策略,请参阅路由与传输。
用户身份
对于 HTTP 请求,内核首先检查arupa-auth 会话 cookie,然后检查 Authorization 标头。有效的会话会解析为一个用户名。内核在 Groups 中查找该用户名,并创建一个经主机验证的身份,其中包含:
- 用户名;
- 包含该用户名的组;以及
Authenticated状态。
策略语义
所有访问检查都使用相同的策略模型:- 空策略是公开的,允许未经身份验证的用户访问。
RequireAuth = true且未指定任何组,允许任何已通过身份验证的用户访问。- 非空的
Groups列表要求用户经过身份验证,并且至少属于列表中的一个组。 - 已通过身份验证但不属于任何列出的组的用户将被禁止访问。
Groups = ["root", "staff"] 允许属于 root 或 staff 组的用户访问;用户无需同时属于这两个组。
两种拒绝状态是不同的:
- 未经身份验证的请求属于身份验证失败,返回
401 Unauthorized。 - 已通过身份验证但不满足组要求的用户属于授权失败,返回
403 Forbidden。
拒绝访问
由于空的Groups 列表是公开的,把 Groups 留空并不能写出“拒绝所有人”的策略——空列表和未设置策略的效果是一样的。要让某条策略真正拒绝所有请求,请指定一个没有任何成员的组,例如 Groups = ["nobody"]。组成员身份完全由 Groups 表自身的成员列表决定,因此策略中引用的组名从不会被校验是否存在;nobody 完全不需要在 [Groups] 中定义。一个未定义的组自然没有任何成员,也就没有任何用户能够属于它,所以它可以直接充当“拒绝所有”的占位符,无需在配置的其他地方做任何额外记录。这一点适用于本页描述的每一层——Route.Allow、服务的 Allow,以及路由自身的 Access 策略,解析组成员身份的方式都是一样的。
主机路由访问
Route.Allow 在请求到达主机处理程序或服务处理程序之前保护 HTTP 路径:
Route.Allow 映射使用内核的共享路径模式实现。普通路径是精确匹配,而以 / 结尾的模式匹配该路径的子树。/* 不是有效的配置,它不支持通配符。
键还可以附加方法限定,做法是在路径前加上一个 HTTP 方法和一个冒号,就像上面的 "GET:/api/list" 那样。带方法限定的规则只适用于使用该方法的请求;不带 METHOD: 前缀的普通路径仍然适用于所有方法。针对 GET 的规则也会匹配 HEAD 请求,因为两者共享缓存语义。方法部分必须是大写的 HTTP 方法名——"get:/api/list" 是无效的,冒号前面什么都不写(例如 ":/api/list")同样无效。Route.Allow 中的每一个键都会被一起校验:只要其中任何一个格式不正确,整个更新就会被拒绝,提交的改动都不会被保存。
当多个 Route.Allow 模式匹配同一个请求时,首先由匹配路径最长的规则获胜。如果有多条规则共享同一个最长路径——比如一条带方法限定的规则和一条普通规则,或者两条针对不同方法的规则——则更具体的方法获胜:精确的方法匹配胜过 GET 规则匹配 HEAD 请求,而后者又胜过不带方法限定的普通规则。内核不会合并多条匹配规则的组列表,只会评估其中一条规则的组列表。完整的路径模式规则、根路径行为和校验细节请参阅路由。
如果没有规则匹配,Route.Allow 不会限制该请求。如果匹配的规则组列表为空,其策略实际上也是公开的,因为空的访问策略允许所有人访问;空列表并不意味着“拒绝所有”。要写出真正拒绝所有请求的规则,请参阅上文的拒绝访问。
外部身份验证中间件会对每个 HTTP 请求评估 Route.Allow。当它拒绝请求时,会返回标准的 JSON 401 或 403 响应,不会使用已配置的 Pages 重定向。关于服务保护的资源所使用的浏览器重定向,请参阅页面。
服务级访问
[Services.<name>] 下的 Allow 适用于整个服务:
Allow 列表表示服务在此层保持开放。非空列表要求用户至少属于列出的组之一。
[Services.default] 提供服务的基础配置。[Services.<name>] 中非空的 Allow 列表会替换该服务的默认组列表。如果某个服务自己的列表被省略或为空,当前实现会保留默认列表。
当内核重新加载配置时,服务策略也会刷新,因此已加载的服务会对后续的请求和事件使用更新后的组列表。
路由访问
服务在注册路由时可以附加另一层策略。这个策略并非写在config.toml 中,而是服务发送给内核的路由声明的一部分,并且无论该路由绑定的是哪种传输,检查方式都相同。
一个 HTTP 路由携带自己的 Access 策略。内核先检查服务级策略,再检查路由级策略:
Access 策略的 HTTP 路由,其检查方式与服务动态处理的路由完全相同。
由于每一层检查都必须通过,有效权限是所有适用层级的交集。例如,用户必须同时属于服务 Allow 中的 staff 组,以及某条路由 Access.Groups 中的 operators 组,才能访问该路由。
示例
使用以下配置:admin 用户可以通过 /api/service/ 主机路由规则,因为其属于 root 组。alice 已通过身份验证,但访问该路径会收到 403 Forbidden,因为她不属于 root 组。未经身份验证的请求会收到 401 Unauthorized。
同一个 admin 用户无法访问任何受 hello 服务 Allow = ["staff"] 保护的路由,因为 admin 不属于 staff 组。当策略指定了具体的组时,仅仅通过身份验证是不够的。