Web 端点的授权
摘要
第 1 章 Web 端点的授权 复制链接链接已复制到粘贴板!
Quarkus 包含可插拔 Web 安全层。当安全性处于活动状态时,系统会对所有 HTTP 请求执行权限检查,以确定它们是否应该继续。
如果使用 Jakarta RESTful Web 服务,请考虑使用 quarkus.security.jaxrs.deny-unannotated-endpoints 或 quarkus.security.jaxrs.default-roles-allowed 来设置默认安全要求而不是 HTTP 路径级别匹配,因为注解可在单个端点上覆盖这些属性。
授权基于安全提供程序提供的用户角色。若要自定义这些角色,可以创建 SecurityIdentityAugmentor,请参阅 安全身份自定义。
1.1. 使用配置进行授权 复制链接链接已复制到粘贴板!
权限通过权限集在 Quarkus 配置中定义,各自为访问控制指定策略。
当安全策略的 paths 属性包含与当前请求路径匹配的最具体路径时,它优先于具有匹配路径的其他安全策略,并被认为是 win。
| 内置策略 | 描述 |
|---|---|
|
| 此策略拒绝所有用户。 |
|
| 此策略允许所有用户。 |
|
| 此策略仅允许经过身份验证的用户。 |
您可以定义基于角色的策略,以允许具有特定角色的用户访问资源。
基于角色的策略示例
quarkus.http.auth.policy.role-policy1.roles-allowed=user,admin
quarkus.http.auth.policy.role-policy1.roles-allowed=user,admin
- 1
- 它定义了一个基于角色的策略,允许用户拥有
用户和admin角色的用户。
您可以通过配置 application.properties 文件中定义的内置权限集来引用自定义策略,如下例所示:
策略配置示例
上例中的具体路径模式 /forbidden 也保护 /forbidden/ 路径。这样,在以下示例中 禁止 的端点通过 deny1 权限进行保护。
- 1
- 为了保护
禁止的端点,需要保护/forbidden和/forbidden/路径。
如果您需要允许访问 /forbidden/ 路径,请添加具有更具体具体路径的新权限,如下例所示:
quarkus.http.auth.permission.permit1.paths=/forbidden/ quarkus.http.auth.permission.permit1.policy=permit
quarkus.http.auth.permission.permit1.paths=/forbidden/
quarkus.http.auth.permission.permit1.policy=permit
- 1
/forbidden/路径不受保护。
1.1.1. Custom HttpSecurityPolicy 复制链接链接已复制到粘贴板!
有时,注册您自己的命名策略可能很有用。您可以通过创建实现 io.quarkus.vertx.http.runtime.security.HttpSecurityPolicy 接口的应用程序范围 CDI bean 来实现它,如下例所示:
- 1
- 命名的 HTTP 安全策略将仅应用到与
application.properties路径匹配规则匹配的请求。
从配置文件引用的名为 HttpSecurityPolicy 的自定义示例
quarkus.http.auth.permission.custom1.paths=/custom/* quarkus.http.auth.permission.custom1.policy=custom
quarkus.http.auth.permission.custom1.paths=/custom/*
quarkus.http.auth.permission.custom1.policy=custom
- 1
- 自定义策略名称必须与
io.quarkus.vertx.http.runtime.security.HttpSecurityPolicy.name方法返回的值匹配。
或者,您可以将名为 HttpSecurityPolicy 的自定义绑定到带有 @AuthorizationPolicy 安全注释的 Jakarta REST 端点。
您还可以在每个请求上创建全局 HttpSecurityPolicy。只需实施 io.quarkus.vertx.http.runtime.security.HttpSecurityPolicy.name 方法,使策略保持无处。
1.1.2. 将 @RequestScoped Bean 注入 HttpSecurityPolicy 复制链接链接已复制到粘贴板!
@RequestScoped Bean 只能在 CDI 请求上下文 处于活跃状态时注入。上下文可以由用户激活,例如使用 @ActivateRequestContext,但授权发生在 Quarkus 准备一些 @RequestScoped Bean 之前发生。我们建议让 Quarkus 激活并为您准备 CDI 请求上下文。例如,请考虑您要从 Jakarta REST 上下文注入 bean 的情况,如 jakarta.ws.rs.core.UriInfo bean。在这种情况下,您必须将 HttpSecurityPolicy 应用到 Jakarta REST 端点。这可以通过以下之一来实现:
-
使用
@AuthorizationPolicy安全注释。 -
设置
quarkus.http.auth.permission.custom1.applies-to=jaxrs配置属性。
1.1.3. 匹配路径和方法 复制链接链接已复制到粘贴板!
权限集也可以将路径和方法指定为用逗号分开的列表。如果路径以 * 通配符结尾,它生成的查询与所有子路径匹配。否则,它会查询一个完全匹配,仅匹配该特定路径:
quarkus.http.auth.permission.permit1.paths=/public*,/css/*,/js/*,/robots.txt quarkus.http.auth.permission.permit1.policy=permit quarkus.http.auth.permission.permit1.methods=GET,HEAD
quarkus.http.auth.permission.permit1.paths=/public*,/css/*,/js/*,/robots.txt
quarkus.http.auth.permission.permit1.policy=permit
quarkus.http.auth.permission.permit1.methods=GET,HEAD
- 1
- 路径末尾的
*通配符匹配零个或更多路径片段,但永远不会从/public路径开始的任何单词。因此,类似/public-info的路径与此模式不匹配。
1.1.4. 匹配路径而不是方法 复制链接链接已复制到粘贴板!
如果请求根据路径匹配一个或多个权限集,则请求将被拒绝,但没有需要的方法。
根据前面的权限集,GET /public/foo 将与路径和方法匹配,因此允许。相反,POST /public/foo 将与路径匹配,而不是方法,因此被拒绝。
1.1.5. 匹配多个路径:最长路径胜出 复制链接链接已复制到粘贴板!
匹配始终以 "longest path wins" 为基础完成。如果多个特定权限集匹配,则不考虑较少的权限集:
根据前面的权限集,GET /public/forbidden-folder/foo 将匹配两个权限集的路径。但是,因为较长的路径与 deny1 权限集的路径匹配,因此选择 deny1,并且请求被拒绝。
在 root 路径权限前的 subPath 权限,因为 deny1 和 permit1 权限示例前面展示了。
此规则进一步加大了在 root 路径权限需要授权时,子路径权限允许访问公共资源的场景。
1.1.6. 匹配多个子路径:到 * 通配符的前一个路径 复制链接链接已复制到粘贴板!
前面的示例演示了,当路径结束带有 * 通配符时,匹配所有子路径。
此通配符也可以在路径的中间应用,代表单个路径段。它不能与其他路径片段字符混合;因此,路径分隔符始终包括 * 通配符,如 /public configured/about-us 路径中所示。
当多个路径模式与同一请求路径对应时,系统会选择前导到 * 通配符的最长子路径。在这种情况下,每个路径片段字符都比 * 通配符更为具体。
下面是一个简单的示例:
quarkus.http.auth.permission.secured.paths=/api/*/detail quarkus.http.auth.permission.secured.policy=authenticated quarkus.http.auth.permission.public.paths=/api/public-product/detail quarkus.http.auth.permission.public.policy=permit
quarkus.http.auth.permission.secured.paths=/api/*/detail
quarkus.http.auth.permission.secured.policy=authenticated
quarkus.http.auth.permission.public.paths=/api/public-product/detail
quarkus.http.auth.permission.public.policy=permit
应测试使用配置进行授权保护的所有路径。使用多个通配符编写路径模式可能会非常繁琐。请确保路径按照预期授权。
在以下示例中,路径从最具体到最具体的路径排序:
请求路径 /one/two/three/four/five 匹配从最具体到最小特定路径的顺序
路径末尾的 * 通配符匹配零个或多个路径片段。* 通配符放置在其它任何位置,与一个路径段完全匹配。
1.1.7. 匹配多个路径:大多数特定方法胜出 复制链接链接已复制到粘贴板!
当使用多个权限集注册路径时,权限集会明确指定与请求具有优先匹配的 HTTP 方法。在本实例中,只有请求方法与方法规格的权限集不匹配时,没有方法的权限集才会生效。
上述权限集显示 GET /public/foo 与这两个权限集的路径匹配。无论如何,它明确与 permit1 权限集的显式方法一致。因此,选择 permit1,并且接受请求。
相反,PUT /public/foo 与 permit1 的方法权限不匹配。因此,deny1 被激活,从而导致请求拒绝。
1.1.8. 匹配多个路径和方法:win 复制链接链接已复制到粘贴板!
有时,之前描述的规则允许同时将多个权限设置为 win。在这种情况下,请求继续,所有权限都必须允许访问权限。要实现此目的,两者必须指定方法或没有方法。特定于方法的匹配具有优先权。
在设置了上述权限集后,GET /api/foo 将匹配两个权限集的路径,需要 用户和 admin 角色。
1.1.9. 拒绝访问的配置属性 复制链接链接已复制到粘贴板!
以下配置设置更改了基于角色的访问控制(RBAC)拒绝行为:
quarkus.security.jaxrs.deny-unannotated-endpoints=true|false-
如果设置为 true,则默认对所有 Jakarta REST 端点访问。如果 Jakarta REST 端点没有安全注解,则默认为
@DenyAll行为。这有助于您避免意外公开应该被保护的端点。默认值为false。 quarkus.security.jaxrs.default-roles-allowed=role1,role2-
定义未注解端点的默认角色要求。
**角色是一个特殊角色,意味着任何经过身份验证的用户。这不能与deny-unannotated-endpoints结合使用,因为deny会生效。 quarkus.security.deny-unannotated-members=true|false-
如果设置为 true,则拒绝所有没有安全注解的 CDI 方法和 Jakarta REST 端点的访问,而是在包含安全注解方法的类中定义。默认值为
false。
1.1.10. 禁用权限 复制链接链接已复制到粘贴板!
可在构建时禁用权限,并为每个声明的权限 启用 属性,例如:
quarkus.http.auth.permission.permit1.enabled=false quarkus.http.auth.permission.permit1.paths=/public/*,/css/*,/js/*,/robots.txt quarkus.http.auth.permission.permit1.policy=permit quarkus.http.auth.permission.permit1.methods=GET,HEAD
quarkus.http.auth.permission.permit1.enabled=false
quarkus.http.auth.permission.permit1.paths=/public/*,/css/*,/js/*,/robots.txt
quarkus.http.auth.permission.permit1.policy=permit
quarkus.http.auth.permission.permit1.methods=GET,HEAD
可以使用系统属性或环境变量在运行时重新启用权限,如: -Dquarkus.http.auth.permission.permit1.enabled=true。
1.1.11. 权限路径和 HTTP 根路径 复制链接链接已复制到粘贴板!
quarkus.http.root-path 配置属性会更改 http 端点上下文路径。
默认情况下,quarkus.http.root-path 会自动添加到配置的权限路径中,然后不使用正斜杠,例如:
quarkus.http.auth.permission.permit1.paths=public/*,css/*,js/*,robots.txt
quarkus.http.auth.permission.permit1.paths=public/*,css/*,js/*,robots.txt
这个配置等同于:
quarkus.http.auth.permission.permit1.paths=${quarkus.http.root-path}/public/*,${quarkus.http.root-path}/css/*,${quarkus.http.root-path}/js/*,${quarkus.http.root-path}/robots.txt
quarkus.http.auth.permission.permit1.paths=${quarkus.http.root-path}/public/*,${quarkus.http.root-path}/css/*,${quarkus.http.root-path}/js/*,${quarkus.http.root-path}/robots.txt
前导斜杠会改变配置的权限路径的解释方式。配置的 URL 以原样使用,如果 quarkus.http.root-path 的值改变,则路径不会被调整。
示例:
quarkus.http.auth.permission.permit1.paths=/public/*,css/*,js/*,robots.txt
quarkus.http.auth.permission.permit1.paths=/public/*,css/*,js/*,robots.txt
此配置仅影响由固定或静态 URL /public 提供的资源,如果 quarkus.http.root-path 已设置为除 / 以外的其他内容,则它可能与您的应用程序资源不匹配。
如需更多信息,请参阅 Quarkus 中的路径解析。
1.1.12. 映射 SecurityIdentity 角色 复制链接链接已复制到粘贴板!
选择授权当前请求的基于角色的策略可将 SecurityIdentity 角色映射到特定于部署的角色。这些角色随后通过使用 @RolesAllowed 注释应用到端点授权。
quarkus.http.auth.policy.admin-policy1.roles.admin=Admin1 quarkus.http.auth.permission.roles1.paths=/* quarkus.http.auth.permission.roles1.policy=admin-policy1
quarkus.http.auth.policy.admin-policy1.roles.admin=Admin1
quarkus.http.auth.permission.roles1.paths=/*
quarkus.http.auth.permission.roles1.policy=admin-policy1
如果您只需要将 SecurityIdentity 角色映射到特定于部署的角色,无论路径是什么,您也可以执行此操作:
quarkus.http.auth.roles-mapping.admin=Admin1
quarkus.http.auth.roles-mapping.admin=Admin1
如果您更喜欢编程配置,可以使用 io.quarkus.vertx.http.security.HttpSecurity CDI 事件添加相同的映射:
1.1.14. 以编程方式设置特定于路径的授权 复制链接链接已复制到粘贴板!
您还可以配置本指南以编程方式提供的授权策略。考虑前面提到的示例:
同一授权策略可以以编程方式配置:
另外,io.quarkus.vertx.http.security.HttpSecurity CDI 事件可用于配置特定的身份验证机制和策略:
- 1
- 使用基本身份验证,并将请求与自定义
io.quarkus.vertx.http.runtime.security.HttpSecurityPolicy授权请求。 - 2
- 使用 Bearer 令牌身份验证,并将
SecurityIdentity与您自己的策略授权。 - 3
- 使用授权代码流机制,并根据传入的请求标头编写您自己的策略。
- 4
- 当 Quarkus 触发
HttpSecurityCDI 事件时,运行时配置已就绪。 - 5
- 要求对
/user-info路径的所有请求都具有字符串权限openid、电子邮件和配置文件。同一授权可能需要使用@PermissionsAllowed (value = { "openid", "email", "profile" }, inclusive = true)注解实例放置在端点上。
1.1.14.1. 编程设置引用 复制链接链接已复制到粘贴板!
1.2. 使用注解进行授权 复制链接链接已复制到粘贴板!
Quarkus 包括内置的安全性,以根据通用安全注释 @RolesAllowed、@DenyAll、@PermitAll on REST 端点和 CDI Bean 来允许 基于角色的访问控制(RBAC)。
在检查标准安全注解前,执行对 quarkus.http.auth. 配置的授权检查。因此,@PermitAll 仅允许访问尚未受 HTTP 权限限制的路径。@PermitAll 无法覆盖 HTTP 级别的安全配置,只有其他标准安全注释(如 @RolesAllowed )实施的限制。
| 注解类型 | 描述 |
|---|---|
|
| 指定不允许安全角色调用指定的方法。 |
|
| 指定允许所有安全角色调用指定的方法。
|
|
| 指定允许访问应用程序中方法的安全角色列表。 |
|
|
Quarkus 提供 |
|
| 指定允许调用指定方法的权限列表。 |
|
|
指定 named |
以下 SubjectExposingResource 示例演示了 一个端点,它使用 Jakarta REST 和 Common Security 注解来描述和保护其端点。
SubjectExposingResource 示例
- 1
/subject/secured端点需要通过使用@RolesAllowed ("Tester")注释而具有授权的"Tester"角色的经过身份验证的用户。- 2
- 端点从 Jakarta REST
SecurityContext获取用户主体。这会为安全端点返回非null。 - 3
/subject/authenticated端点可通过指定@Authenticated注释来允许任何经过身份验证的用户。- 4
/subject/unsecured端点通过指定@PermitAll注释来允许未经身份验证的访问。- 5
- 如果调用者被验证,则获取用户主体的调用会返回
。null - 6
/subject/denied端点声明@DenyAll注释,不允许以 REST 方法直接访问它,而不考虑调用它的用户。此方法仍然可由此类中的其他方法在内部执行。
如果您计划在 IO 线程上使用标准安全注解,请查看 主动身份验证 中的信息。
@RolesAllowed 注释值支持属性 表达式,包括默认值和嵌套属性表达式。与注解搭配使用的配置属性会在运行时解决。
| 注解 | 值解释 |
|---|---|
|
|
端点允许具有由 |
|
| 显示该值可包含多个变量的示例。 |
|
|
默认值演示。所需的角色由 |
@RolesAllowed 注解中的属性表达式使用示例
主题访问控制示例
- 1
@RolesAllowed注释值设置为Administrator的值。- 2
- 此
/subject/software-tester端点需要一个经过身份验证的用户,该用户被授予了"Software-Tester"的角色。可以在角色定义中使用多个表达式。 - 3
- 此
/subject/user端点需要一个经过身份验证的用户,该用户已通过使用@RolesAllowed ("${customer:User}")注解授予角色"User",因为我们没有设置配置属性客户。 - 4
- 在生产环境中,这个
/subject/secured端点需要一个具有User角色的经过身份验证的用户。在开发模式中,它允许任何经过身份验证的用户。 - 5
- 属性表达式
all-roles将被视为集合类型列表,因此可为角色管理员、软件、测试程序和用户访问端点。
1.2.1. 端点安全注解和 Jakarta REST 继承 复制链接链接已复制到粘贴板!
Quarkus 支持放置在端点实现或其类上,如下例所示:
声明为默认接口方法的 RESTEasy 子资源 locators 无法被标准安全注解保护。必须在接口实现器上实现并保护安全的子资源 locators,如下例所示:
1.2.2. 权限注解 复制链接链接已复制到粘贴板!
Quarkus 还提供 io.quarkus.security.PermissionsAllowed 注解,它会授权任何具有给定权限的用户访问资源。此注解是常见安全注解的扩展,检查授予了 SecurityIdentity 实例的权限。
使用 @PermissionsAllowed 注释保护的端点示例
- 1
- 资源方法
createOrUpdate只能被具有create和update权限的用户访问。 - 2
- 默认情况下,至少需要通过一个注解实例指定的权限之一。您可以通过设置
inclusive=true来要求所有权限。两种资源方法createOrUpdate都有相等的授权要求。 - 3
- 如果
SecurityIdentity有读取权限,则授予getItem权限或查看权限以及all或detail操作之一。 - 4
- 您可以使用您首选的
java.security.Permission实现。默认情况下,基于字符串的权限由io.quarkus.security.StringPermission执行。 - 5
- 权限不是 Bean,因此获取 bean 实例的唯一方式是使用
Arc.container ()以编程方式。
如果您计划在 IO 线程上使用 @PermissionsAllowed,请查看 主动身份验证 中的信息。
由于 Quarkus 拦截器的限制,@PermissionsAllowed 在类级别上不能可重复。如需更多信息,请参阅 Quarkus "CDI 参考"指南中的 Repeatable interceptor bindings 部分。
为启用了角色的 SecurityIdentity 实例添加权限的最简单方法是将角色映射到权限。使用 Authorization using configuration 将 CRUDResource 端点所需的 SecurityIdentity 权限授予经过身份验证的用户,如下例所示:
- 1
- 将权限
see和all添加到用户角色的SecurityIdentity实例中。同样,对于@PermissionsAllowed注释,默认使用io.quarkus.security.StringPermission。 - 2
- 权限
创建、update和read被映射到角色admin。 - 3
- 4
- 您可以指定
java.security.Permission类的自定义实现。自定义类必须精确定义一个构造器,它接受权限名称和可选的一些操作,如String数组。在这种情况下,权限列表被添加到SecurityIdentity实例中,作为新的 CustomPermission ("。list")
您还可以使用额外的构造器参数创建自定义 java.security.Permission 类。这些附加参数名称与标上 @PermissionsAllowed 注释的方法的参数名称匹配。之后,Quarkus 使用实际参数实例化您的自定义权限,其中调用了 @PermissionsAllowed 注解的方法。
接受额外参数的自定义 java.security.Permission 类示例
- 1
- 自定义
权限类必须只有一个构造器。第一个参数始终被视为权限名称,必须是String类型。Quarkus 可以选择性地将权限操作传递给构造器。为此,请将第二个参数声明为String[]。
如果允许 SecurityIdentity 执行其中一个必要操作(如 读取、写入 或 列表 ),则 LibraryPermission 类允许访问当前或父库。
以下示例演示了如何使用 LibraryPermission 类:
使用 LibraryPermission保护的资源示例
与 CRUDResource 示例类似,以下示例演示了如何为用户授予具有 admin 角色的用户更新 MediaLibrary :
quarkus.http.auth.policy.role-policy3.permissions.admin=media-library:list,media-library:read,media-library:write quarkus.http.auth.policy.role-policy3.permission-class=org.acme.library.MediaLibraryPermission quarkus.http.auth.permission.roles3.paths=/library/* quarkus.http.auth.permission.roles3.policy=role-policy3
quarkus.http.auth.policy.role-policy3.permissions.admin=media-library:list,media-library:read,media-library:write
quarkus.http.auth.policy.role-policy3.permission-class=org.acme.library.MediaLibraryPermission
quarkus.http.auth.permission.roles3.paths=/library/*
quarkus.http.auth.permission.roles3.policy=role-policy3
- 1
- 授予权限
media-library,允许读取、写入和列出操作。因为MediaLibrary是TvLibrary类父级,因此还允许具有admin角色的用户修改TvLibrary。
可以在 Keycloak 供应商 Dev UI 页面中测试 /library RAID 路径,因为用户 alice 由 Keycloak 的 Dev Services 自动创建且具有 admin 角色。
目前提供的示例演示有角色到权限映射。也可以以编程方式向 SecurityIdentity 实例添加权限。在以下示例中,自定义SecurityIdentity 来添加之前通过 HTTP 角色策略授予的相同权限。
以编程方式将 LibraryPermission 添加到 SecurityIdentity的示例
- 1
- 添加可执行
读取、写入和列出操作的media-library权限。因为MediaLibrary是TvLibrary类父级,因此还允许具有admin角色的用户修改TvLibrary。
基于注解的权限不适用于自定义 Jakarta REST SecurityContexts,因为 jakarta.ws.rs.core.SecurityContext 中没有权限。
1.2.2.1. 创建权限检查器 复制链接链接已复制到粘贴板!
默认情况下,SecurityIdentity 必须配置有权限,可用于检查此身份是否通过 @PermissionAllowed 授权限制。或者,您可以使用 @PermissionChecker 注释将任何 CDI bean 方法标记为权限检查程序。@PermissionChecker 注释值应与 @PermissionsAllowed 注释值声明所需的权限匹配。例如,可以创建权限检查程序,如下所示:
权限检查器方法可以在普通范围的 CDI Bean 或 @Singleton bean 上声明。目前不支持 @Dependent CDI bean 范围。
上面的权限检查程序需要 SecurityIdentity 实例授权 renameProject 端点。您可以在资源上直接声明 rename-project 权限检查程序,您可以在任何 CDI bean 上声明它,如下例所示:
默认情况下,权限检查在事件循环上运行。如果要在 worker 线程上运行检查,使用 io.smallrye.common.annotation.Blocking 注解注解权限检查程序方法。
@PermissionsAllowed 值和 @PermissionChecker 值之间的匹配基于字符串 equality,如下例所示:
1.2.2.2. 创建权限 meta-annotations 复制链接链接已复制到粘贴板!
@PermissionsAllowed 也可以用于 meta-annotations。例如,可以创建一个新的 @CanWrite 安全注解,如下所示:
- 1
- 使用
@CanWrite注释标注的任何方法或类都通过@PermissionsAllowed注释实例进行保护。
1.2.2.3. 将 @BeanParam 参数传递给自定义权限 复制链接链接已复制到粘贴板!
Quarkus 可以将安全方法参数的字段映射到自定义权限构造器参数。您可以使用此功能将 jakarta.ws.rs.BeanParam 参数传递给您的自定义权限。让我们考虑以下 Jakarta REST 资源:
- 1
params注解属性指定用户主体名称应传递给BeanParamPermissionChecker#canSayHello方法。其他BeanParamPermissionChecker#canSayHello方法参数(如customAuthorizationHeader和查询)会自动匹配。Quarkus 在beanParam字段及其公共访问器之间标识BeanParamPermissionChecker#canSayHello方法参数。为避免模糊解析,自动检测仅适用于beanParam字段。因此,我们必须明确指定用户主体名称的路径。
其中声明了 SimpleBeanParam 类,如下例所示:
下面是一个 @PermissionChecker 方法的示例,它根据用户主体、自定义标头和查询参数检查 say-hello 权限:
您可以将 @BeanParam 直接传递给 @PermissionChecker 方法,并以编程方式访问其字段。当您有多个不同的结构化 @BeanParam 类时,使用 @PermissionsAllowed Serialparams 属性引用 @BeanParam 字段的功能很有用。