Web 端点的授权


Red Hat build of Quarkus 3.27

Red Hat Customer Content Services

摘要

本指南探索 Web 端点的授权机制,专注于基于配置和基于注解的方法。它检查配置授权,包括使用内置和自定义策略、路径和方法匹配以及处理复杂路径场景。然后,它被分成基于角色的访问控制和权限管理的细微,涵盖了访问拒绝、禁用权限并将角色映射到 SecurityIdentity 的属性。介绍了使用注解保护 RESTful 服务、突出显示标准安全注解及其应用程序的信息。

第 1 章 Web 端点的授权

Quarkus 包含可插拔 Web 安全层。当安全性处于活动状态时,系统会对所有 HTTP 请求执行权限检查,以确定它们是否应该继续。

注意

如果使用 Jakarta RESTful Web 服务,请考虑使用 quarkus.security.jaxrs.deny-unannotated-endpointsquarkus.security.jaxrs.default-roles-allowed 来设置默认安全要求而不是 HTTP 路径级别匹配,因为注解可在单个端点上覆盖这些属性。

授权基于安全提供程序提供的用户角色。若要自定义这些角色,可以创建 SecurityIdentityAugmentor,请参阅 安全身份自定义

1.1. 使用配置进行授权

权限通过权限集在 Quarkus 配置中定义,各自为访问控制指定策略。

注意

当安全策略的 paths 属性包含与当前请求路径匹配的最具体路径时,它优先于具有匹配路径的其他安全策略,并被认为是 win。

Expand
表 1.1. Quarkus 策略概述
内置策略描述

deny

此策略拒绝所有用户。

permit

此策略允许所有用户。

authenticated

此策略仅允许经过身份验证的用户。

您可以定义基于角色的策略,以允许具有特定角色的用户访问资源。

基于角色的策略示例

quarkus.http.auth.policy.role-policy1.roles-allowed=user,admin                  
1
Copy to Clipboard Toggle word wrap

1
它定义了一个基于角色的策略,允许用户拥有 用户和 admin 角色的用户。

您可以通过配置 application.properties 文件中定义的内置权限集来引用自定义策略,如下例所示:

策略配置示例

quarkus.http.auth.permission.permit1.paths=/public/*                            
1

quarkus.http.auth.permission.permit1.policy=permit
quarkus.http.auth.permission.permit1.methods=GET

quarkus.http.auth.permission.deny1.paths=/forbidden                             
2

quarkus.http.auth.permission.deny1.policy=deny

quarkus.http.auth.permission.roles1.paths=/roles-secured/*,/other/*,/api/*      
3

quarkus.http.auth.permission.roles1.policy=role-policy1
Copy to Clipboard Toggle word wrap

1
此权限引用了默认的内置 允许 策略,以允许 GET 方法访问 /public。在这种情况下,演示的设置不会影响此示例,因为允许此请求。
2
这个权限引用了 /forbidden 和 /forbidden / 路径的内置 拒绝 策略。它是一个精确的路径匹配,因为它不以 * 结尾。
3
此权限集引用之前定义的策略。roles1 是一个示例名称;您可以调用您想要的权限集。
重要

上例中的具体路径模式 /forbidden 也保护 /forbidden/ 路径。这样,在以下示例中 禁止 的端点通过 deny1 权限进行保护。

package org.acme.crud;

import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;

@Path("/forbidden")
public class ForbiddenResource {
    @GET
    public String forbidden() { 
1

        return "No!";
    }
}
Copy to Clipboard Toggle word wrap
1
为了保护 禁止 的端点,需要保护 /forbidden/forbidden/ 路径。

如果您需要允许访问 /forbidden/ 路径,请添加具有更具体具体路径的新权限,如下例所示:

quarkus.http.auth.permission.permit1.paths=/forbidden/ 
1

quarkus.http.auth.permission.permit1.policy=permit
Copy to Clipboard Toggle word wrap
1
/forbidden/ 路径不受保护。

1.1.1. Custom HttpSecurityPolicy

有时,注册您自己的命名策略可能很有用。您可以通过创建实现 io.quarkus.vertx.http.runtime.security.HttpSecurityPolicy 接口的应用程序范围 CDI bean 来实现它,如下例所示:

import jakarta.enterprise.context.ApplicationScoped;

import io.quarkus.security.identity.SecurityIdentity;
import io.quarkus.vertx.http.runtime.security.HttpSecurityPolicy;
import io.smallrye.mutiny.Uni;
import io.vertx.ext.web.RoutingContext;

@ApplicationScoped
public class CustomNamedHttpSecPolicy implements HttpSecurityPolicy {
    @Override
    public Uni<CheckResult> checkPermission(RoutingContext event, Uni<SecurityIdentity> identity,
            AuthorizationRequestContext requestContext) {
        if (customRequestAuthorization(event)) {
            return CheckResult.permit();
        }
        return CheckResult.deny();
    }

    @Override
    public String name() {
        return "custom"; 
1

    }

    private static boolean customRequestAuthorization(RoutingContext event) {
        // here comes your own security check
        return !event.request().path().endsWith("denied");
    }
}
Copy to Clipboard Toggle word wrap
1
命名的 HTTP 安全策略将仅应用到与 application.properties 路径匹配规则匹配的请求。

从配置文件引用的名为 HttpSecurityPolicy 的自定义示例

quarkus.http.auth.permission.custom1.paths=/custom/*
quarkus.http.auth.permission.custom1.policy=custom                              
1
Copy to Clipboard Toggle word wrap

1
自定义策略名称必须与 io.quarkus.vertx.http.runtime.security.HttpSecurityPolicy.name 方法返回的值匹配。

或者,您可以将名为 HttpSecurityPolicy 的自定义绑定到带有 @AuthorizationPolicy 安全注释的 Jakarta REST 端点。

绑定到 Jakarta REST 端点的自定义名为 HttpSecurityPolicy 的示例

import io.quarkus.vertx.http.security.AuthorizationPolicy;
import jakarta.annotation.security.DenyAll;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;

@DenyAll 
1

@Path("hello")
public class HelloResource {

    @AuthorizationPolicy(name = "custom") 
2

    @GET
    public String hello() {
        return "hello";
    }

}
Copy to Clipboard Toggle word wrap

1
@AuthorizationPolicy 注释可以与其他标准安全注释一起使用。通常,方法级注释的优先级高于类级注释。
2
将名为 HttpSecurityPolicy 的自定义应用到 Jakarta REST hello 端点。
提示

您还可以在每个请求上创建全局 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    
1

quarkus.http.auth.permission.permit1.policy=permit
quarkus.http.auth.permission.permit1.methods=GET,HEAD
Copy to Clipboard Toggle word wrap
1
路径末尾的 * 通配符匹配零个或更多路径片段,但永远不会从 /public 路径开始的任何单词。因此,类似 /public-info 的路径与此模式不匹配。

1.1.4. 匹配路径而不是方法

如果请求根据路径匹配一个或多个权限集,则请求将被拒绝,但没有需要的方法。

提示

根据前面的权限集,GET /public/foo 将与路径和方法匹配,因此允许。相反,POST /public/foo 将与路径匹配,而不是方法,因此被拒绝。

1.1.5. 匹配多个路径:最长路径胜出

匹配始终以 "longest path wins" 为基础完成。如果多个特定权限集匹配,则不考虑较少的权限集:

quarkus.http.auth.permission.permit1.paths=/public/*
quarkus.http.auth.permission.permit1.policy=permit
quarkus.http.auth.permission.permit1.methods=GET,HEAD

quarkus.http.auth.permission.deny1.paths=/public/forbidden-folder/*
quarkus.http.auth.permission.deny1.policy=deny
Copy to Clipboard Toggle word wrap
提示

根据前面的权限集,GET /public/forbidden-folder/foo 将匹配两个权限集的路径。但是,因为较长的路径与 deny1 权限集的路径匹配,因此选择 deny1,并且请求被拒绝。

注意

在 root 路径权限前的 subPath 权限,因为 deny1permit1 权限示例前面展示了。

此规则进一步加大了在 root 路径权限需要授权时,子路径权限允许访问公共资源的场景。

quarkus.http.auth.policy.user-policy.roles-allowed=user
quarkus.http.auth.permission.roles.paths=/api/*
quarkus.http.auth.permission.roles.policy=user-policy

quarkus.http.auth.permission.public.paths=/api/noauth/*
quarkus.http.auth.permission.public.policy=permit
Copy to Clipboard Toggle word wrap

前面的示例演示了,当路径结束带有 * 通配符时,匹配所有子路径。

此通配符也可以在路径的中间应用,代表单个路径段。它不能与其他路径片段字符混合;因此,路径分隔符始终包括 * 通配符,如 /public configured/about-us 路径中所示。

当多个路径模式与同一请求路径对应时,系统会选择前导到 * 通配符的最长子路径。在这种情况下,每个路径片段字符都比 * 通配符更为具体。

下面是一个简单的示例:

quarkus.http.auth.permission.secured.paths=/api/*/detail                    
1

quarkus.http.auth.permission.secured.policy=authenticated
quarkus.http.auth.permission.public.paths=/api/public-product/detail        
2

quarkus.http.auth.permission.public.policy=permit
Copy to Clipboard Toggle word wrap
1
请求路径(如 /api/product/detail )只能被经过身份验证的用户访问。
2
路径 /api/public-product/detail 更为具体,因此任何人都可以访问。
重要

应测试使用配置进行授权保护的所有路径。使用多个通配符编写路径模式可能会非常繁琐。请确保路径按照预期授权。

在以下示例中,路径从最具体到最具体的路径排序:

请求路径 /one/two/three/four/five 匹配从最具体到最小特定路径的顺序

/one/two/three/four/five
/one/two/three/four/*
/one/two/three/*/five
/one/two/three/*/*
/one/two/*/four/five
/one/*/three/four/five
/*/two/three/four/five
/*/two/three/*/five
/*
Copy to Clipboard Toggle word wrap

重要

路径末尾的 * 通配符匹配零个或多个路径片段。* 通配符放置在其它任何位置,与一个路径段完全匹配。

1.1.7. 匹配多个路径:大多数特定方法胜出

当使用多个权限集注册路径时,权限集会明确指定与请求具有优先匹配的 HTTP 方法。在本实例中,只有请求方法与方法规格的权限集不匹配时,没有方法的权限集才会生效。

quarkus.http.auth.permission.permit1.paths=/public/*
quarkus.http.auth.permission.permit1.policy=permit
quarkus.http.auth.permission.permit1.methods=GET,HEAD

quarkus.http.auth.permission.deny1.paths=/public/*
quarkus.http.auth.permission.deny1.policy=deny
Copy to Clipboard Toggle word wrap
注意

上述权限集显示 GET /public/foo 与这两个权限集的路径匹配。无论如何,它明确与 permit1 权限集的显式方法一致。因此,选择 permit1,并且接受请求。

相反,PUT /public/foopermit1 的方法权限不匹配。因此,deny1 被激活,从而导致请求拒绝。

1.1.8. 匹配多个路径和方法:win

有时,之前描述的规则允许同时将多个权限设置为 win。在这种情况下,请求继续,所有权限都必须允许访问权限。要实现此目的,两者必须指定方法或没有方法。特定于方法的匹配具有优先权。

quarkus.http.auth.policy.user-policy1.roles-allowed=user
quarkus.http.auth.policy.admin-policy1.roles-allowed=admin

quarkus.http.auth.permission.roles1.paths=/api/*,/restricted/*
quarkus.http.auth.permission.roles1.policy=user-policy1

quarkus.http.auth.permission.roles2.paths=/api/*,/admin/*
quarkus.http.auth.permission.roles2.policy=admin-policy1
Copy to Clipboard Toggle word wrap
提示

在设置了上述权限集后,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
Copy to Clipboard Toggle word wrap

可以使用系统属性或环境变量在运行时重新启用权限,如: -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
Copy to Clipboard Toggle word wrap

这个配置等同于:

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
Copy to Clipboard Toggle word wrap

前导斜杠会改变配置的权限路径的解释方式。配置的 URL 以原样使用,如果 quarkus.http.root-path 的值改变,则路径不会被调整。

示例:

quarkus.http.auth.permission.permit1.paths=/public/*,css/*,js/*,robots.txt
Copy to Clipboard Toggle word wrap

此配置仅影响由固定或静态 URL /public 提供的资源,如果 quarkus.http.root-path 已设置为除 / 以外的其他内容,则它可能与您的应用程序资源不匹配。

如需更多信息,请参阅 Quarkus 中的路径解析

1.1.12. 映射 SecurityIdentity 角色

选择授权当前请求的基于角色的策略可将 SecurityIdentity 角色映射到特定于部署的角色。这些角色随后通过使用 @RolesAllowed 注释应用到端点授权。

quarkus.http.auth.policy.admin-policy1.roles.admin=Admin1 
1

quarkus.http.auth.permission.roles1.paths=/* 
2

quarkus.http.auth.permission.roles1.policy=admin-policy1
Copy to Clipboard Toggle word wrap
1
admin 角色映射到 Admin1 角色。SecurityIdentity 将具有 adminAdmin1 角色。
2
jakarta 路径 是安全的,只有经过身份验证的 HTTP 请求才会授予访问权限。

如果您只需要将 SecurityIdentity 角色映射到特定于部署的角色,无论路径是什么,您也可以执行此操作:

quarkus.http.auth.roles-mapping.admin=Admin1 
1
 
2
Copy to Clipboard Toggle word wrap
1
admin 角色映射到 Admin1 角色。SecurityIdentity 将具有 adminAdmin1 角色。
2
Thycotic 路径不受保护。您必须使用标准安全注解保护端点,或者除了此配置属性外定义 HTTP 权限。

如果您更喜欢编程配置,可以使用 io.quarkus.vertx.http.security.HttpSecurity CDI 事件添加相同的映射:

package org.acme.http.security;

import io.quarkus.vertx.http.security.HttpSecurity;
import jakarta.enterprise.event.Observes;

public class HttpSecurityConfiguration {

    void configure(@Observes HttpSecurity httpSecurity) {
        httpSecurity.rolesMapping("admin", "Admin1");
    }
}
Copy to Clipboard Toggle word wrap

1.1.13. 共享权限检查

非共享权限检查的一个重要规则是仅应用一个路径匹配(最具体的路径匹配)。当路径匹配为最具体时,您可以为该路径指定多个权限,并且会应用它们。但是,您可以对许多路径应用权限检查,而无需再次重复它们。也就是说,当权限路径匹配时,会始终应用共享权限检查。

在每个 HTTP 请求中应用名为 HttpSecurityPolicy 的自定义示例

quarkus.http.auth.permission.custom1.paths=/*
quarkus.http.auth.permission.custom1.shared=true    
1

quarkus.http.auth.permission.custom1.policy=custom

quarkus.http.auth.policy.admin-policy1.roles-allowed=admin
quarkus.http.auth.permission.roles1.paths=/admin/*
quarkus.http.auth.permission.roles1.policy=admin-policy1
Copy to Clipboard Toggle word wrap

1
自定义 HttpSecurityPolicy 也应用于 /admin/1 路径以及 admin-policy1 策略。
提示

配置许多共享权限检查比配置未共享的权限检查效率较低。使用共享权限来补充类似以下示例中的非共享权限检查。

使用共享权限映射 SecurityIdentity 角色

quarkus.http.auth.policy.role-policy1.roles.root=admin,user 
1

quarkus.http.auth.permission.roles1.paths=/secured/*        
2

quarkus.http.auth.permission.roles1.policy=role-policy1
quarkus.http.auth.permission.roles1.shared=true

quarkus.http.auth.policy.role-policy2.roles-allowed=user    
3

quarkus.http.auth.permission.roles2.paths=/secured/user/*
quarkus.http.auth.permission.roles2.policy=role-policy2

quarkus.http.auth.policy.role-policy3.roles-allowed=admin
quarkus.http.auth.permission.roles3.paths=/secured/admin/*
quarkus.http.auth.permission.roles3.policy=role-policy3
Copy to Clipboard Toggle word wrap

1
角色 root 能够访问 /secured/user configured/secured/admin86] 路径。
2
/secured 86] 路径只能被经过身份验证的用户访问。这样,您已保护 /secured/all 路径,以此类推。
3
在未共享共享权限之前,始终应用共享权限,因此具有 root 角色的 SecurityIdentity 也具有用户角色。

1.1.14. 以编程方式设置特定于路径的授权

您还可以配置本指南以编程方式提供的授权策略。考虑前面提到的示例:

quarkus.http.auth.permission.permit1.paths=/public/*
quarkus.http.auth.permission.permit1.policy=permit
quarkus.http.auth.permission.permit1.methods=GET

quarkus.http.auth.permission.deny1.paths=/forbidden
quarkus.http.auth.permission.deny1.policy=deny

quarkus.http.auth.permission.roles1.paths=/roles-secured/*,/other/*,/api/*
quarkus.http.auth.permission.roles1.policy=role-policy1
quarkus.http.auth.policy.role-policy1.roles-allowed=user,admin
Copy to Clipboard Toggle word wrap

同一授权策略可以以编程方式配置:

package org.acme.http.security;

import jakarta.enterprise.event.Observes;

import io.quarkus.vertx.http.security.HttpSecurity;

public class HttpSecurityConfiguration {

    void configure(@Observes HttpSecurity httpSecurity) {
        httpSecurity
                .get("/public/*").permit()
                .path("/roles-secured/*", "/other/*", "/api/*").roles("admin", "user")
                .path("/forbidden").authorization().deny();
    }

}
Copy to Clipboard Toggle word wrap

另外,io.quarkus.vertx.http.security.HttpSecurity CDI 事件可用于配置特定的身份验证机制和策略:

package org.acme.http.security;

import io.quarkus.vertx.http.security.HttpSecurity;
import jakarta.enterprise.event.Observes;
import org.eclipse.microprofile.config.inject.ConfigProperty;

public class HttpSecurityConfiguration {

    void configure(@Observes HttpSecurity httpSecurity, CustomHttpSecurityPolicy customHttpSecurityPolicy,
                        @ConfigProperty(name = "secured-path") String securedPath) {

        httpSecurity.path("/other/*").basic().policy(customHttpSecurityPolicy); 
1


        httpSecurity.path("/roles-secured/*").bearer().authorization()
                .policy(identity -> identity.hasRole("user") || "root".equals(identity.getPrincipal().getName()));  
2


        httpSecurity.path("/other/administration").authorizationCodeFlow()
                .authorization().policy((identity, routingContext) -> {
                    if (!identity.isAnonymous()) {
                        String customAuthorization = routingContext.request().getHeader("Custom Authorization");
                        return yourCustomAuthorizationCheck(customAuthorization);
                    }
                    return false;
                });     
3


        httpSecurity.path(securedPath).form();  
4


        httpSecurity.path("/user-info").bearer().authorization().permissions("openid", "email", "profile"); 
5

    }
}
Copy to Clipboard Toggle word wrap
1
使用基本身份验证,并将请求与自定义 io.quarkus.vertx.http.runtime.security.HttpSecurityPolicy 授权请求。
2
使用 Bearer 令牌身份验证,并将 SecurityIdentity 与您自己的策略授权。
3
使用授权代码流机制,并根据传入的请求标头编写您自己的策略。
4
当 Quarkus 触发 HttpSecurity CDI 事件时,运行时配置已就绪。
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 )实施的限制。

Expand
表 1.2. Quarkus 注解类型概述
注解类型描述

@DenyAll

指定不允许安全角色调用指定的方法。

@PermitAll

指定允许所有安全角色调用指定的方法。

@PermitAll 可让任何人都有,即使没有身份验证。

@RolesAllowed

指定允许访问应用程序中方法的安全角色列表。

@authenticated

Quarkus 提供 io.quarkus.security.Authenticated 注解,允许任何经过身份验证的用户访问该资源。等同于 @RolesAllowed ("**")

@PermissionsAllowed

指定允许调用指定方法的权限列表。

@AuthorizationPolicy

指定 named io.quarkus.vertx.http.runtime.security.HttpSecurityPolicy,它应授权访问指定的 Jakarta REST 端点。名为 HttpSecurityPolicy 可以用来进行常规授权检查,如自定义 named HttpSecurityPolicy 绑定到 Jakarta REST 端点的 示例所示。

以下 SubjectExposingResource 示例演示了 一个端点,它使用 Jakarta REST 和 Common Security 注解来描述和保护其端点。

SubjectExposingResource 示例

import java.security.Principal;

import jakarta.annotation.security.DenyAll;
import jakarta.annotation.security.PermitAll;
import jakarta.annotation.security.RolesAllowed;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.core.Context;
import jakarta.ws.rs.core.SecurityContext;

@Path("subject")
public class SubjectExposingResource {

    @GET
    @Path("secured")
    @RolesAllowed("Tester") 
1

    public String getSubjectSecured(@Context SecurityContext sec) {
        Principal user = sec.getUserPrincipal(); 
2

        String name = user != null ? user.getName() : "anonymous";
        return name;
    }

    @GET
    @Path("authenticated")
    @Authenticated 
3

    public String getSubjectAuthenticated(@Context SecurityContext sec) {
        Principal user = sec.getUserPrincipal();
        String name = user != null ? user.getName() : "anonymous";
        return name;
    }

    @GET
    @Path("unsecured")
    @PermitAll 
4

    public String getSubjectUnsecured(@Context SecurityContext sec) {
        Principal user = sec.getUserPrincipal(); 
5

        String name = user != null ? user.getName() : "anonymous";
        return name;
    }

    @GET
    @Path("denied")
    @DenyAll 
6

    public String getSubjectDenied(@Context SecurityContext sec) {
        Principal user = sec.getUserPrincipal();
        String name = user != null ? user.getName() : "anonymous";
        return name;
    }
}
Copy to Clipboard Toggle word wrap

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 方法直接访问它,而不考虑调用它的用户。此方法仍然可由此类中的其他方法在内部执行。
Important

如果您计划在 IO 线程上使用标准安全注解,请查看 主动身份验证 中的信息。

@RolesAllowed 注释值支持属性 表达式,包括默认值和嵌套属性表达式。与注解搭配使用的配置属性会在运行时解决。

Expand
表 1.3. 注解值示例
注解值解释

@RolesAllowed("${admin-role}")

端点允许具有由 admin-role 属性的值表示的角色的用户。

@RolesAllowed("${tester.group}-${tester.role}")

显示该值可包含多个变量的示例。

@RolesAllowed("${customer:User}")

默认值演示。所需的角色由 customer 属性的值表示。但是,如果没有指定该属性,则默认需要名为 User 的角色。

@RolesAllowed 注解中的属性表达式使用示例

admin=Administrator
tester.group=Software
tester.role=Tester
%prod.secured=User
%dev.secured=**
all-roles=Administrator,Software,Tester,User
Copy to Clipboard Toggle word wrap

主题访问控制示例

import java.security.Principal;

import jakarta.annotation.security.RolesAllowed;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.core.Context;
import jakarta.ws.rs.core.SecurityContext;

@Path("subject")
public class SubjectExposingResource {

    @GET
    @Path("admin")
    @RolesAllowed("${admin}") 
1

    public String getSubjectSecuredAdmin(@Context SecurityContext sec) {
        return getUsername(sec);
    }

    @GET
    @Path("software-tester")
    @RolesAllowed("${tester.group}-${tester.role}") 
2

    public String getSubjectSoftwareTester(@Context SecurityContext sec) {
        return getUsername(sec);
    }

    @GET
    @Path("user")
    @RolesAllowed("${customer:User}") 
3

    public String getSubjectUser(@Context SecurityContext sec) {
        return getUsername(sec);
    }

    @GET
    @Path("secured")
    @RolesAllowed("${secured}") 
4

    public String getSubjectSecured(@Context SecurityContext sec) {
        return getUsername(sec);
    }

    @GET
    @Path("list")
    @RolesAllowed("${all-roles}") 
5

    public String getSubjectList(@Context SecurityContext sec) {
        return getUsername(sec);
    }

    private String getUsername(SecurityContext sec) {
        Principal user = sec.getUserPrincipal();
        String name = user != null ? user.getName() : "anonymous";
        return name;
    }
}
Copy to Clipboard Toggle word wrap

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 支持放置在端点实现或其类上,如下例所示:

@Path("hello")
public interface HelloInterface {

    @GET
    String hello();

}

@DenyAll 
1

public class HelloInterfaceImpl implements HelloInterface {

    @RolesAllowed("admin") 
2

    @Override
    public String hello() {
        return "Hello";
    }
}
Copy to Clipboard Toggle word wrap
1
类级别的安全注解必须放在声明端点实施的类中。
2
方法级安全注解必须放在端点实现中。
重要

声明为默认接口方法的 RESTEasy 子资源 locators 无法被标准安全注解保护。必须在接口实现器上实现并保护安全的子资源 locators,如下例所示:

@Path("hello")
public interface HelloInterface {

    @RolesAllowed("admin")
    @Path("sub")
    default HelloSubResource wrongWay() {
        // not supported
    }

    @Path("sub")
    HelloSubResource rightWay();

}

public class HelloInterfaceImpl implements HelloInterface {

    @RolesAllowed("admin")
    @Override
    public HelloSubResource rightWay() {
        return new HelloSubResource();
    }
}
Copy to Clipboard Toggle word wrap

1.2.2. 权限注解

Quarkus 还提供 io.quarkus.security.PermissionsAllowed 注解,它会授权任何具有给定权限的用户访问资源。此注解是常见安全注解的扩展,检查授予了 SecurityIdentity 实例的权限。

使用 @PermissionsAllowed 注释保护的端点示例

package org.acme.crud;

import io.quarkus.arc.Arc;
import io.vertx.ext.web.RoutingContext;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.QueryParam;

import io.quarkus.security.PermissionsAllowed;

import java.security.BasicPermission;
import java.security.Permission;
import java.util.Collection;
import java.util.Collections;

@Path("/crud")
public class CRUDResource {

    @PermissionsAllowed("create") 
1

    @PermissionsAllowed("update")
    @POST
    @Path("/modify/repeated")
    public String createOrUpdate() {
        return "modified";
    }

    @PermissionsAllowed(value = {"create", "update"}, inclusive=true) 
2

    @POST
    @Path("/modify/inclusive")
    public String createOrUpdate(Long id) {
        return id + " modified";
    }

    @PermissionsAllowed({"see:detail", "see:all", "read"}) 
3

    @GET
    @Path("/id/{id}")
    public String getItem(String id) {
        return "item-detail-" + id;
    }

    @PermissionsAllowed(value = "list", permission = CustomPermission.class) 
4

    @Path("/list")
    @GET
    public Collection<String> list(@QueryParam("query-options") String queryOptions) {
        // your business logic comes here
        return Collections.emptySet();
    }

    public static class CustomPermission extends BasicPermission {

        public CustomPermission(String name) {
            super(name);
        }

        @Override
        public boolean implies(Permission permission) {
            var event = Arc.container().instance(RoutingContext.class).get(); 
5

            var publicContent = "public-content".equals(event.request().params().get("query-options"));
            var hasPermission = getName().equals(permission.getName());
            return hasPermission && publicContent;
        }
    }
}
Copy to Clipboard Toggle word wrap

1
资源方法 createOrUpdate 只能被具有 createupdate 权限的用户访问。
2
默认情况下,至少需要通过一个注解实例指定的权限之一。您可以通过设置 inclusive=true 来要求所有权限。两种资源方法 createOrUpdate 都有相等的授权要求。
3
如果 SecurityIdentity读取权限,则授予 getItem 权限 或查看 权限以及 alldetail 操作之一。
4
您可以使用您首选的 java.security.Permission 实现。默认情况下,基于字符串的权限由 io.quarkus.security.StringPermission 执行。
5
权限不是 Bean,因此获取 bean 实例的唯一方式是使用 Arc.container () 以编程方式。
Important

如果您计划在 IO 线程上使用 @PermissionsAllowed,请查看 主动身份验证 中的信息。

注意

由于 Quarkus 拦截器的限制,@PermissionsAllowed 在类级别上不能可重复。如需更多信息,请参阅 Quarkus "CDI 参考"指南中的 Repeatable interceptor bindings 部分。

为启用了角色的 SecurityIdentity 实例添加权限的最简单方法是将角色映射到权限。使用 Authorization using configurationCRUDResource 端点所需的 SecurityIdentity 权限授予经过身份验证的用户,如下例所示:

quarkus.http.auth.policy.role-policy1.permissions.user=see:all                                      
1

quarkus.http.auth.policy.role-policy1.permissions.admin=create,update,read                          
2

quarkus.http.auth.permission.roles1.paths=/crud/modify/*,/crud/id/*                                 
3

quarkus.http.auth.permission.roles1.policy=role-policy1

quarkus.http.auth.policy.role-policy2.permissions.user=list
quarkus.http.auth.policy.role-policy2.permission-class=org.acme.crud.CRUDResource$CustomPermission  
4

quarkus.http.auth.permission.roles2.paths=/crud/list
quarkus.http.auth.permission.roles2.policy=role-policy2
Copy to Clipboard Toggle word wrap
1
将权限 seeall 添加到 用户角色的 SecurityIdentity 实例中。同样,对于 @PermissionsAllowed 注释,默认使用 io.quarkus.security.StringPermission
2
权限 创建updateread 被映射到角色 admin
3
角色策略 role-policy1 只允许经过身份验证的请求访问 /crud/modify/crud/id 子路径。有关路径匹配算法的更多信息,请参阅本指南的 后文中匹配多个路径:最长路径
4
您可以指定 java.security.Permission 类的自定义实现。自定义类必须精确定义一个构造器,它接受权限名称和可选的一些操作,如 String 数组。在这种情况下,权限列表被添加到 SecurityIdentity 实例中,作为 新的 CustomPermission (" list ")

您还可以使用额外的构造器参数创建自定义 java.security.Permission 类。这些附加参数名称与标上 @PermissionsAllowed 注释的方法的参数名称匹配。之后,Quarkus 使用实际参数实例化您的自定义权限,其中调用了 @PermissionsAllowed 注解的方法。

接受额外参数的自定义 java.security.Permission 类示例

package org.acme.library;

import java.security.Permission;
import java.util.Arrays;
import java.util.Set;

public class LibraryPermission extends Permission {

    private final Set<String> actions;
    private final Library library;

    public LibraryPermission(String libraryName, String[] actions, Library library) { 
1

        super(libraryName);
        this.actions = Set.copyOf(Arrays.asList(actions));
        this.library = library;
    }

    @Override
    public boolean implies(Permission requiredPermission) {
        if (requiredPermission instanceof LibraryPermission) {
            LibraryPermission that = (LibraryPermission) requiredPermission;
            boolean librariesMatch = getName().equals(that.getName());
            boolean requiredLibraryIsSublibrary = library.isParentLibraryOf(that.library);
            boolean hasOneOfRequiredActions = that.actions.stream().anyMatch(actions::contains);
            return (librariesMatch || requiredLibraryIsSublibrary) && hasOneOfRequiredActions;
        }
        return false;
    }

    // here comes your own implementation of the `java.security.Permission` class methods

    public static abstract class Library {

        protected String description;

        abstract boolean isParentLibraryOf(Library library);

    }

    public static class MediaLibrary extends Library {

        @Override
        boolean isParentLibraryOf(Library library) {
            return library instanceof MediaLibrary;
        }
    }

    public static class TvLibrary extends MediaLibrary {
        // TvLibrary specific implementation of the 'isParentLibraryOf' method
    }
}
Copy to Clipboard Toggle word wrap

1
自定义 权限 类必须只有一个构造器。第一个参数始终被视为权限名称,必须是 String 类型。Quarkus 可以选择性地将权限操作传递给构造器。为此,请将第二个参数声明为 String[]

如果允许 SecurityIdentity 执行其中一个必要操作(如 读取写入列表 ),则 LibraryPermission 类允许访问当前或父库。

以下示例演示了如何使用 LibraryPermission 类:

package org.acme.library;

import io.quarkus.security.PermissionsAllowed;
import jakarta.enterprise.context.ApplicationScoped;
import org.acme.library.LibraryPermission.Library;

@ApplicationScoped
public class LibraryService {

    @PermissionsAllowed(value = "tv:write", permission = LibraryPermission.class) 
1

    public Library updateLibrary(String newDesc, Library library) {
        library.description = newDesc;
        return library;
    }

    @PermissionsAllowed(value = "tv:write", permission = LibraryPermission.class) 
2

    @PermissionsAllowed(value = {"tv:read", "tv:list"}, permission = LibraryPermission.class)
    public Library migrateLibrary(Library migrate, Library library) {
        // migrate libraries
        return library;
    }

}
Copy to Clipboard Toggle word wrap
1
正式参数 被识别为与同名 库Permission 构造器参数匹配的参数。因此,Quarkus 会将 library 参数传递给 LibraryPermission 类构造器。但是,每次调用 updateLibrary 方法时,都必须实例化 LibraryPermission
2
此处,第二个 Library 参数与名称 匹配,而 migrate 参数则在 LibraryPermission 权限实例化期间会被忽略。

使用 LibraryPermission保护的资源示例

package org.acme.library;

import io.quarkus.security.PermissionsAllowed;
import jakarta.inject.Inject;
import jakarta.ws.rs.PUT;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import org.acme.library.LibraryPermission.Library;

@Path("/library")
public class LibraryResource {

    @Inject
    LibraryService libraryService;

    @PermissionsAllowed(value = "tv:write", permission = LibraryPermission.class)
    @PUT
    @Path("/id/{id}")
    public Library updateLibrary(@PathParam("id") Integer id, Library library) {
        ...
    }

    @PUT
    @Path("/service-way/id/{id}")
    public Library updateLibrarySvc(@PathParam("id") Integer id, Library library) {
        String newDescription = "new description " + id;
        return libraryService.updateLibrary(newDescription, library);
    }

}
Copy to Clipboard Toggle word wrap

CRUDResource 示例类似,以下示例演示了如何为用户授予具有 admin 角色的用户更新 MediaLibrary

package org.acme.library;

import io.quarkus.runtime.annotations.RegisterForReflection;

@RegisterForReflection 
1

public class MediaLibraryPermission extends LibraryPermission {

    public MediaLibraryPermission(String libraryName, String[] actions) {
        super(libraryName, actions, new MediaLibrary());    
2

    }

}
Copy to Clipboard Toggle word wrap
1
在构建原生可执行文件时,必须注册优先级类以进行反映,除非至少在至少一个 io.quarkus.security.PermissionsAllowed"name 参数中使用。有关 @RegisterForReflection 注释的更多详细信息,请参阅 原生应用程序提示 页面。
2
我们希望将 MediaLibrary 实例传递给 LibraryPermission 构造器。
quarkus.http.auth.policy.role-policy3.permissions.admin=media-library:list,media-library:read,media-library:write   
1

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
Copy to Clipboard Toggle word wrap
1
授予权限 media-library,允许 读取写入 和列出 操作。因为 MediaLibraryTvLibrary 类父级,因此还允许具有 admin 角色的用户修改 TvLibrary
提示

可以在 Keycloak 供应商 Dev UI 页面中测试 /library RAID 路径,因为用户 aliceKeycloak 的 Dev Services 自动创建且具有 admin 角色。

目前提供的示例演示有角色到权限映射。也可以以编程方式向 SecurityIdentity 实例添加权限。在以下示例中,自定义SecurityIdentity 来添加之前通过 HTTP 角色策略授予的相同权限。

以编程方式将 LibraryPermission 添加到 SecurityIdentity的示例

import java.security.Permission;

import jakarta.enterprise.context.ApplicationScoped;

import io.quarkus.security.identity.AuthenticationRequestContext;
import io.quarkus.security.identity.SecurityIdentity;
import io.quarkus.security.identity.SecurityIdentityAugmentor;
import io.quarkus.security.runtime.QuarkusSecurityIdentity;
import io.smallrye.mutiny.Uni;

@ApplicationScoped
public class PermissionsIdentityAugmentor implements SecurityIdentityAugmentor {

    @Override
    public Uni<SecurityIdentity> augment(SecurityIdentity identity, AuthenticationRequestContext context) {
        if (isNotAdmin(identity)) {
            return Uni.createFrom().item(identity);
        }
        return Uni.createFrom().item(build(identity));
    }

    private boolean isNotAdmin(SecurityIdentity identity) {
        return identity.isAnonymous() || !"admin".equals(identity.getPrincipal().getName());
    }

    SecurityIdentity build(SecurityIdentity identity) {
        return QuarkusSecurityIdentity.builder(identity)
                .addPermission(new MediaLibraryPermission("media-library", new String[] { "read", "write", "list"}); 
1

                .build();
    }

}
Copy to Clipboard Toggle word wrap

1
添加可执行 读取写入 和列出 操作的 media-library 权限。因为 MediaLibraryTvLibrary 类父级,因此还允许具有 admin 角色的用户修改 TvLibrary
Important

基于注解的权限不适用于自定义 Jakarta REST SecurityContexts,因为 jakarta.ws.rs.core.SecurityContext 中没有权限。

1.2.2.1. 创建权限检查器

默认情况下,SecurityIdentity 必须配置有权限,可用于检查此身份是否通过 @PermissionAllowed 授权限制。或者,您可以使用 @PermissionChecker 注释将任何 CDI bean 方法标记为权限检查程序。@PermissionChecker 注释值应与 @PermissionsAllowed 注释值声明所需的权限匹配。例如,可以创建权限检查程序,如下所示:

package org.acme.security.rest.resource;

import io.quarkus.security.PermissionChecker;
import io.quarkus.security.PermissionsAllowed;
import io.quarkus.security.identity.SecurityIdentity;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;

import org.jboss.resteasy.reactive.RestForm;
import org.jboss.resteasy.reactive.RestPath;

@Path("/project/{projectName}")
public class ProjectResource {

    @PermissionsAllowed("rename-project") 
1

    @POST
    public void renameProject(@RestPath String projectName, @RestForm String newName) {
        Project project = Project.findByName(projectName);
        project.name = newName;
    }

    @PermissionChecker("rename-project") 
2

    boolean canRenameProject(SecurityIdentity identity, String projectName) { 
3
 
4

        var principalName = identity.getPrincipal().getName();
        var user = User.getUserByName(principalName);
        return userOwnsProject(projectName, user);
    }
}
Copy to Clipboard Toggle word wrap
1
访问 ProjectResource-<renameProject 所需的权限是 rename-project 权限。
2
ProjectResource#canRenameProject 方法授权访问 ProjectResource:renameProject 端点。
3
SecurityIdentity 实例可以注入到任何权限检查程序方法中。
4
在本例中,同一资源上声明 rename-project 权限检查程序。但是,在下一个示例中,无法声明此权限检查程序。
注意

权限检查器方法可以在普通范围的 CDI Bean 或 @Singleton bean 上声明。目前不支持 @Dependent CDI bean 范围。

上面的权限检查程序需要 SecurityIdentity 实例授权 renameProject 端点。您可以在资源上直接声明 rename-project 权限检查程序,您可以在任何 CDI bean 上声明它,如下例所示:

package org.acme.security.rest.resource;

import io.quarkus.security.PermissionChecker;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;

@ApplicationScoped 
1

public class ProjectPermissionChecker {

    @PermissionChecker("rename-project")
    boolean canRenameProject(String projectName, SecurityIdentity identity) {   
2

        var principalName = identity.getPrincipal().getName();
        var user = User.getUserByName(principalName);
        return userOwnsProject(projectName, user);
    }

}
Copy to Clipboard Toggle word wrap
1
具有权限检查器的 CDI bean 必须是普通范围的 bean 或 @Singleton bean。
2
权限检查器方法必须返回 布尔值Uni<Boolean>。不支持私有检查程序方法。
提示

默认情况下,权限检查在事件循环上运行。如果要在 worker 线程上运行检查,使用 io.smallrye.common.annotation.Blocking 注解注解权限检查程序方法。

@PermissionsAllowed 值和 @PermissionChecker 值之间的匹配基于字符串 equality,如下例所示:

package org.acme.security;

import io.quarkus.security.PermissionChecker;
import io.quarkus.security.PermissionsAllowed;
import jakarta.enterprise.context.ApplicationScoped;

@ApplicationScoped
public class FileService {

    @PermissionsAllowed({ "delete:all", "delete:dir" }) 
1

    void deleteDirectory(Path directoryPath) {
        // delete directory
    }

    @PermissionsAllowed(value = { "delete:service", "delete:file" }, inclusive = true) 
2

    void deleteServiceFile(Path serviceFilePath) {
        // delete service file
    }

    @PermissionChecker("delete:all")
    boolean canDeleteAllDirectories(SecurityIdentity identity) {
        String filePermissions = identity.getAttribute("user-group-file-permissions");
        return filePermissions != null && filePermissions.contains("w");
    }

    @PermissionChecker("delete:service")
    boolean canDeleteService(SecurityIdentity identity) {
        return identity.hasRole("admin");
    }

    @PermissionChecker("delete:file")
    boolean canDeleteFile(Path serviceFilePath) {
        return serviceFilePath != null && !serviceFilePath.endsWith("critical");
    }
}
Copy to Clipboard Toggle word wrap
1
权限检查器方法 canDeleteAllDirectories 授予对 deleteDirectory 的访问权限,因为 delete:all 值是相等的。
2
必须正好有两个权限检查程序方法,一个用于 delete:service 权限,另一个用于 delete:file 权限。
1.2.2.2. 创建权限 meta-annotations

@PermissionsAllowed 也可以用于 meta-annotations。例如,可以创建一个新的 @CanWrite 安全注解,如下所示:

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import io.quarkus.security.PermissionsAllowed;

@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.TYPE })
@PermissionsAllowed(value = "write", permission = CustomPermission.class) 
1

public @interface CanWrite {

}
Copy to Clipboard Toggle word wrap
1
使用 @CanWrite 注释标注的任何方法或类都通过 @PermissionsAllowed 注释实例进行保护。
1.2.2.3. 将 @BeanParam 参数传递给自定义权限

Quarkus 可以将安全方法参数的字段映射到自定义权限构造器参数。您可以使用此功能将 jakarta.ws.rs.BeanParam 参数传递给您的自定义权限。让我们考虑以下 Jakarta REST 资源:

package org.acme.security.rest.resource;

import io.quarkus.security.PermissionsAllowed;
import jakarta.ws.rs.BeanParam;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;

@Path("/hello")
public class HelloResource {

    @PermissionsAllowed(value = "say-hello", params = "beanParam.securityContext.userPrincipal.name") 
1

    @GET
    public String sayHello(@BeanParam SimpleBeanParam beanParam) {
        return "Hello from " + beanParam.uriInfo.getPath();
    }

}
Copy to Clipboard Toggle word wrap
1
params 注解属性指定用户主体名称应传递给 BeanParamPermissionChecker#canSayHello 方法。其他 BeanParamPermissionChecker#canSayHello 方法参数(如 customAuthorizationHeader查询 )会自动匹配。Quarkus 在 beanParam 字段及其公共访问器之间标识 BeanParamPermissionChecker#canSayHello 方法参数。为避免模糊解析,自动检测仅适用于 beanParam 字段。因此,我们必须明确指定用户主体名称的路径。

其中声明了 SimpleBeanParam 类,如下例所示:

package org.acme.security.rest.dto;

import java.util.List;

import jakarta.ws.rs.HeaderParam;
import jakarta.ws.rs.QueryParam;
import jakarta.ws.rs.core.Context;
import jakarta.ws.rs.core.SecurityContext;
import jakarta.ws.rs.core.UriInfo;

public class SimpleBeanParam {

    @HeaderParam("CustomAuthorization")
    private String customAuthorizationHeader;

    @Context
    SecurityContext securityContext;

    @Context
    public UriInfo uriInfo;

    @QueryParam("query")
    public String query;    
1


    public SecurityContext getSecurityContext() { 
2

        return securityContext;
    }

    public String customAuthorizationHeader() { 
3

        return customAuthorizationHeader;
    }
}
Copy to Clipboard Toggle word wrap
1
Quarkus Security 只能将公共字段传递给自定义权限构造器。
2
如果可用,quarkus Security 会自动使用公共 getter 方法。
3
customAuthorizationHeader 字段不是公共的,因此 Quarkus 使用 customAuthorizationHeader accessor 访问此字段。这对 Java 记录特别有用,其中生成的访问器不是前缀。

下面是一个 @PermissionChecker 方法的示例,它根据用户主体、自定义标头和查询参数检查 say-hello 权限:

package org.acme.security.permission;

import jakarta.enterprise.context.ApplicationScoped;
import io.quarkus.security.PermissionChecker;

@ApplicationScoped
public class BeanParamPermissionChecker {

    @PermissionChecker("say-hello")
    boolean canSayHello(String customAuthorizationHeader, String name, String query) {
        boolean queryParamAllowedForPermissionName = checkQueryParams(query);
        boolean usernameWhitelisted = isUserNameWhitelisted(name);
        boolean customAuthorizationMatches = checkCustomAuthorization(customAuthorizationHeader);
        return queryParamAllowedForPermissionName && usernameWhitelisted && customAuthorizationMatches;
    }

    ...
}
Copy to Clipboard Toggle word wrap
注意

您可以将 @BeanParam 直接传递给 @PermissionChecker 方法,并以编程方式访问其字段。当您有多个不同的结构化 @BeanParam 类时,使用 @PermissionsAllowed Serialparams 属性引用 @BeanParam 字段的功能很有用。

1.3. 参考

法律通告

Copyright © 2025 Red Hat, Inc.
The text of and illustrations in this document are licensed by Red Hat under a Creative Commons Attribution–Share Alike 3.0 Unported license ("CC-BY-SA"). An explanation of CC-BY-SA is available at http://creativecommons.org/licenses/by-sa/3.0/. In accordance with CC-BY-SA, if you distribute this document or an adaptation of it, you must provide the URL for the original version.
Red Hat, as the licensor of this document, waives the right to enforce, and agrees not to assert, Section 4d of CC-BY-SA to the fullest extent permitted by applicable law.
Red Hat, Red Hat Enterprise Linux, the Shadowman logo, the Red Hat logo, JBoss, OpenShift, Fedora, the Infinity logo, and RHCE are trademarks of Red Hat, Inc., registered in the United States and other countries.
Linux® is the registered trademark of Linus Torvalds in the United States and other countries.
Java® is a registered trademark of Oracle and/or its affiliates.
XFS® is a trademark of Silicon Graphics International Corp. or its subsidiaries in the United States and/or other countries.
MySQL® is a registered trademark of MySQL AB in the United States, the European Union and other countries.
Node.js® is an official trademark of Joyent. Red Hat is not formally related to or endorsed by the official Joyent Node.js open source or commercial project.
The OpenStack® Word Mark and OpenStack logo are either registered trademarks/service marks or trademarks/service marks of the OpenStack Foundation, in the United States and other countries and are used with the OpenStack Foundation's permission. We are not affiliated with, endorsed or sponsored by the OpenStack Foundation, or the OpenStack community.
All other trademarks are the property of their respective owners.
返回顶部
Red Hat logoGithubredditYoutubeTwitter

学习

尝试、购买和销售

社区

关于红帽文档

通过我们的产品和服务,以及可以信赖的内容,帮助红帽用户创新并实现他们的目标。 了解我们当前的更新.

让开源更具包容性

红帽致力于替换我们的代码、文档和 Web 属性中存在问题的语言。欲了解更多详情,请参阅红帽博客.

關於紅帽

我们提供强化的解决方案,使企业能够更轻松地跨平台和环境(从核心数据中心到网络边缘)工作。

Theme

© 2025 Red Hat