编辑此页面

安全提示和技巧

Quarkus 安全依赖

io.quarkus:quarkus-security 模块包含核心 Quarkus 安全类。

在大多数情况下,不需要将其直接添加到项目的构建文件中,因为它已经由所有安全扩展提供。但是,如果您需要编写自己的自定义安全代码(例如,注册 自定义 Jakarta REST SecurityContext)或使用 BouncyCastle 库,请确保包含它

pom.xml
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-security</artifactId>
</dependency>
build.gradle
implementation("io.quarkus:quarkus-security")

HttpAuthenticationMechanism 自定义

可以通过注册 CDI 实现 bean 来定制 HttpAuthenticationMechanism。在下面的示例中,自定义验证器委托给 quarkus-smallrye-jwt 提供的 JWTAuthMechanism

@Alternative
@Priority(1)
@ApplicationScoped
public class CustomAwareJWTAuthMechanism implements HttpAuthenticationMechanism {

	private static final Logger LOG = LoggerFactory.getLogger(CustomAwareJWTAuthMechanism.class);

	@Inject
	JWTAuthMechanism delegate;

	@Override
	public Uni<SecurityIdentity> authenticate(RoutingContext context, IdentityProviderManager identityProviderManager) {
	    // do some custom action and delegate
            return delegate.authenticate(context, identityProviderManager);
	}

	@Override
	public Uni<ChallengeData> getChallenge(RoutingContext context) {
		return delegate.getChallenge(context);
	}

	@Override
	public Set<Class<? extends AuthenticationRequest>> getCredentialTypes() {
		return delegate.getCredentialTypes();
	}

	@Override
	public Uni<HttpCredentialTransport> getCredentialTransport() {
		return delegate.getCredentialTransport();
	}

}
HttpAuthenticationMechanism 应该将具有适当身份验证凭据的传入 HTTP 请求转换为 io.quarkus.security.identity.request.AuthenticationRequest 实例,并将身份验证委托给 io.quarkus.security.identity.IdentityProviderManager。将身份验证留给 io.quarkus.security.identity.IdentityProvider 可以为您提供更多凭据验证选项,以及执行阻塞任务的便捷方法。尽管如此,可以省略 io.quarkus.security.identity.IdentityProvider,并且 HttpAuthenticationMechanism 可以自由地在简单用例中自行验证请求。

处理多个 HttpAuthenticationMechanism

可以组合多个 HttpAuthenticationMechanism,例如,必须使用 quarkus-smallrye-jwt 提供的内置 BasicJWT 机制来验证作为 HTTP Authorization BasicBearer 方案值传递的服务客户端凭据,而必须使用 quarkus-oidc 提供的 Authorization Code 机制来验证使用 Keycloak 或其他 OpenID Connect 提供程序的用户的身份。

在这种情况下,会依次询问这些机制以验证凭据,直到创建 SecurityIdentity。这些机制按照优先级降序排序。Basic 身份验证机制的优先级最高,为 2000,其次是优先级为 1001Authorization Code 身份验证机制,Quarkus 提供的所有其他机制的优先级为 1000

如果没有提供凭据,则会创建机制特定的质询,例如,BasicJWT 机制返回 401 状态,将用户重定向到 OpenID Connect 提供程序的 URL 由 quarkus-oidc 返回,等等。

因此,如果组合了 BasicAuthorization Code 机制,则如果未提供凭据,则将返回 401,如果组合了 JWTAuthorization Code 机制,则将返回重定向 URL。

在某些情况下,这种选择质询的默认逻辑正是给定应用程序所需要的,但有时可能无法满足要求。在这种情况下(或者实际上在您想要更改机制处理当前身份验证或质询请求的顺序的其他类似情况下),您可以创建一个自定义机制并选择哪个机制应该创建质询,例如

@Alternative (1)
@Priority(1)
@ApplicationScoped
public class CustomAwareJWTAuthMechanism implements HttpAuthenticationMechanism {

	private static final Logger LOG = LoggerFactory.getLogger(CustomAwareJWTAuthMechanism.class);

	@Inject
	JWTAuthMechanism jwt;

        @Inject
	OidcAuthenticationMechanism oidc;

	@Override
	public Uni<SecurityIdentity> authenticate(RoutingContext context, IdentityProviderManager identityProviderManager) {
	    return selectBetweenJwtAndOidc(context).authenticate(context, identityProviderManager);
	}

	@Override
	public Uni<ChallengeData> getChallenge(RoutingContext context) {
            return selectBetweenJwtAndOidcChallenge(context).getChallenge(context);
	}

	@Override
	public Set<Class<? extends AuthenticationRequest>> getCredentialTypes() {
            Set<Class<? extends AuthenticationRequest>> credentialTypes = new HashSet<>();
            credentialTypes.addAll(jwt.getCredentialTypes());
            credentialTypes.addAll(oidc.getCredentialTypes());
            return credentialTypes;
	}

        @Override
        public Uni<HttpCredentialTransport> getCredentialTransport(RoutingContext context) {
            return selectBetweenJwtAndOidc(context).getCredentialTransport(context);
        }

        private HttpAuthenticationMechanism selectBetweenJwtAndOidc(RoutingContext context) {
            ....
        }

        private HttpAuthenticationMechanism selectBetweenJwtAndOidcChallenge(RoutingContext context) {
            // for example, if no `Authorization` header is available and no `code` parameter is provided - use `jwt` to create a challenge
        }

}
1 将机制声明为备用 bean 可确保使用此机制而不是 OidcAuthenticationMechanismJWTAuthMechanism

安全身份定制

在内部,身份提供程序创建并更新 io.quarkus.security.identity.SecurityIdentity 类的实例,该实例保存主体、角色、用于验证客户端(用户)身份的凭据和其他安全属性。定制 SecurityIdentity 的一个简单选项是注册自定义 SecurityIdentityAugmentor。例如,下面的增强器添加了一个附加角色

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;

import jakarta.enterprise.context.ApplicationScoped;
import java.util.function.Supplier;

@ApplicationScoped
public class RolesAugmentor implements SecurityIdentityAugmentor {

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

    private Supplier<SecurityIdentity> build(SecurityIdentity identity) {
        if(identity.isAnonymous()) {
            return () -> identity;
        } else {
            // create a new builder and copy principal, attributes, credentials and roles from the original identity
            QuarkusSecurityIdentity.Builder builder = QuarkusSecurityIdentity.builder(identity);

            // add custom role source here
            builder.addRole("dummy");
            return builder::build;
        }
    }
}
1 增强安全身份。有关如何在阻塞模式下增强身份的示例,请参见激活请求上下文部分。

这是另一个示例,展示了如何使用当前相互 TLS (mTLS) 身份验证请求中可用的客户端证书来添加更多角色

import java.security.cert.X509Certificate;
import io.quarkus.security.credential.CertificateCredential;
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;

import jakarta.enterprise.context.ApplicationScoped;
import java.util.function.Supplier;
import java.util.Set;

@ApplicationScoped
public class RolesAugmentor implements SecurityIdentityAugmentor {

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

    private Supplier<SecurityIdentity> build(SecurityIdentity identity) {
        // create a new builder and copy principal, attributes, credentials and roles from the original identity
        QuarkusSecurityIdentity.Builder builder = QuarkusSecurityIdentity.builder(identity);

        CertificateCredential certificate = identity.getCredential(CertificateCredential.class);
        if (certificate != null) {
            builder.addRoles(extractRoles(certificate.getCertificate()));
        }
        return builder::build;
    }

    private Set<String> extractRoles(X509Certificate certificate) {
        String name = certificate.getSubjectX500Principal().getName();

        switch (name) {
            case "CN=client":
                return Collections.singleton("user");
            case "CN=guest-client":
                return Collections.singleton("guest");
            default:
                return Collections.emptySet();
        }
    }
}

如果注册了多个自定义 SecurityIdentityAugmentor,则它们将被视为相等的候选者并以随机顺序调用。您可以通过实现默认的 SecurityIdentityAugmentor#priority 方法来强制执行该顺序。具有较高优先级的增强器将首先被调用。

激活请求上下文

默认情况下,在增强安全身份时不会激活请求上下文,这意味着如果您想使用例如 Hibernate 强制执行请求上下文,您将遇到 jakarta.enterprise.context.ContextNotActiveException

另请查看“主动身份验证”指南的激活 CDI 请求上下文部分。

解决方案是激活请求上下文,以下示例展示了如何从带有 Panache UserRoleEntity 的 Hibernate 获取角色。

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;

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

@ApplicationScoped
public class RolesAugmentor implements SecurityIdentityAugmentor {

    @Inject
    UserEntityAugmentor userEntityAugmentor;

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

        // Hibernate ORM is blocking
        return context.runBlocking(() -> userEntityAugmentor.augment(identity));
    }
}
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.context.control.ActivateRequestContext;

import io.quarkus.security.identity.SecurityIdentity;
import io.quarkus.security.runtime.QuarkusSecurityIdentity;

@ApplicationScoped
class UserEntityAugmentor {

    @ActivateRequestContext
    public SecurityIdentity augment(SecurityIdentity identity) {
        QuarkusSecurityIdentity.Builder builder = QuarkusSecurityIdentity.builder(identity);
        String user = identity.getPrincipal().getName();

        UserRoleEntity.<userRoleEntity>streamAll()
                .filter(role -> user.equals(role.user))
                .forEach(role -> builder.addRole(role.role));

        return builder.build();
    }
}

上面示例中显示的 CDI 请求上下文激活无法帮助您在启用主动身份验证时访问 RoutingContext。以下示例说明了如何从 SecurityIdentityAugmentor 访问 RoutingContext

package org.acme.security;

import java.util.Map;

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.vertx.http.runtime.security.HttpSecurityUtils;
import io.smallrye.mutiny.Uni;
import io.vertx.ext.web.RoutingContext;

@ApplicationScoped
public class CustomSecurityIdentityAugmentor implements SecurityIdentityAugmentor {

    @Override
    public Uni<SecurityIdentity> augment(SecurityIdentity identity, AuthenticationRequestContext context,
            Map<String, Object> attributes) {
        RoutingContext routingContext = HttpSecurityUtils.getRoutingContextAttribute(attributes);
        if (routingContext != null) {
            // Augment SecurityIdentity using RoutingContext
        } else {
            return augment(identity, context); (1)
        }
    }

    ...
}
1 在 HTTP 请求完成后增强 SecurityIdentity 时,RoutingContext 将不可用。
如果您实现了自定义 HttpAuthenticationMechanism,则需要使用 io.quarkus.vertx.http.runtime.security.HttpSecurityUtils.setRoutingContextAttribute 方法调用将 RoutingContext 添加到身份验证请求属性中。否则,在增强期间将无法使用 RoutingContext

自定义 Jakarta REST SecurityContext

如果您使用 Jakarta REST ContainerRequestFilter 来设置自定义 Jakarta REST SecurityContext,请确保 ContainerRequestFilter 通过向其添加 @PreMatching 注释在 Jakarta REST 预匹配阶段运行,以便将此自定义安全上下文与 Quarkus SecurityIdentity 链接,例如

import java.security.Principal;

import jakarta.ws.rs.container.ContainerRequestContext;
import jakarta.ws.rs.container.ContainerRequestFilter;
import jakarta.ws.rs.container.PreMatching;
import jakarta.ws.rs.core.SecurityContext;
import jakarta.ws.rs.ext.Provider;

@Provider
@PreMatching
public class SecurityOverrideFilter implements ContainerRequestFilter {
    @Override
    public void filter(ContainerRequestContext requestContext) throws IOException {
        String user = requestContext.getHeaders().getFirst("User");
        String role = requestContext.getHeaders().getFirst("Role");
        if (user != null && role != null) {
            requestContext.setSecurityContext(new SecurityContext() {
                @Override
                public Principal getUserPrincipal() {
                    return new Principal() {
                        @Override
                        public String getName() {
                            return user;
                        }
                    };
                }

                @Override
                public boolean isUserInRole(String r) {
                    return role.equals(r);
                }

                @Override
                public boolean isSecure() {
                    return false;
                }

                @Override
                public String getAuthenticationScheme() {
                    return "basic";
                }
            });
        }

    }
}

禁用授权

如果您有充分的理由禁用授权,则可以注册自定义 AuthorizationController

@Alternative
@Priority(Interceptor.Priority.LIBRARY_AFTER)
@ApplicationScoped
public class DisabledAuthController extends AuthorizationController {
    @ConfigProperty(name = "disable.authorization", defaultValue = "false")
    boolean disableAuthorization;

    @Override
    public boolean isAuthorizationEnabled() {
        return !disableAuthorization;
    }
}

对于手动测试,Quarkus 提供了一个方便的配置属性,可以在开发模式下禁用授权。此属性与上面显示的自定义 AuthorizationController 具有完全相同的效果,但仅在开发模式下可用

quarkus.security.auth.enabled-in-dev-mode=false

另请参见 TestingSecurity Annotation 部分,了解如何使用 TestSecurity 注释禁用安全检查。

注册安全提供程序

默认提供程序

以本机模式运行时,GraalVM 本机可执行文件生成的默认行为是仅包含主要的“SUN”提供程序,除非您启用了 SSL,在这种情况下,将注册所有安全提供程序。如果您未使用 SSL,则可以使用 quarkus.security.security-providers 属性按名称选择性地注册安全提供程序。以下示例说明了注册“SunRsaSign”和“SunJCE”安全提供程序的配置

安全提供程序配置示例
quarkus.security.security-providers=SunRsaSign,SunJCE

BouncyCastle

如果您需要注册 org.bouncycastle.jce.provider.BouncyCastleProvider JCE 提供程序,请设置一个 BC 提供程序名称

安全提供程序 BouncyCastle 配置示例
quarkus.security.security-providers=BC

并添加 BouncyCastle 提供程序依赖项

pom.xml
<dependency>
    <groupId>org.bouncycastle</groupId>
    <artifactId>bcprov-jdk18on</artifactId>
</dependency>
build.gradle
implementation("org.bouncycastle:bcprov-jdk18on")

BouncyCastle JSSE

如果您需要注册 org.bouncycastle.jsse.provider.BouncyCastleJsseProvider JSSE 提供程序并使用它而不是默认的 SunJSSE 提供程序,请设置一个 BCJSSE 提供程序名称

安全提供程序 BouncyCastle JSSE 配置示例
quarkus.security.security-providers=BCJSSE

quarkus.http.ssl.client-auth=REQUIRED

quarkus.http.ssl.certificate.key-store-file=server-keystore.jks
quarkus.http.ssl.certificate.key-store-password=password
quarkus.http.ssl.certificate.trust-store-file=server-truststore.jks
quarkus.http.ssl.certificate.trust-store-password=password

并添加 BouncyCastle TLS 依赖项

pom.xml
<dependency>
    <groupId>org.bouncycastle</groupId>
    <artifactId>bctls-jdk18on</artifactId>
</dependency>
build.gradle
implementation("org.bouncycastle:bctls-jdk18on")

BouncyCastle FIPS

如果您需要注册 org.bouncycastle.jcajce.provider.BouncyCastleFipsProvider JCE 提供程序,请设置一个 BCFIPS 提供程序名称

安全提供程序 BouncyCastle FIPS 配置示例
quarkus.security.security-providers=BCFIPS

并添加 BouncyCastle FIPS 提供程序依赖项

pom.xml
<dependency>
    <groupId>org.bouncycastle</groupId>
    <artifactId>bc-fips</artifactId>
</dependency>
build.gradle
implementation("org.bouncycastle:bc-fips")

本机映像支持 BCFIPS 提供程序选项,但依赖于 java.security.SecureRandom 来验证生成的密钥的算法自测已删除,以便这些测试能够通过。以下类受到影响:- org.bouncycastle.crypto.general.DSA - org.bouncycastle.crypto.general.DSTU4145 - org.bouncycastle.crypto.general.ECGOST3410 - org.bouncycastle.crypto.general.GOST3410 - org.bouncycastle.crypto.fips.FipsDSA - org.bouncycastle.crypto.fips.FipsEC - org.bouncycastle.crypto.fips.FipsRSA

BouncyCastle JSSE FIPS

如果您需要注册 org.bouncycastle.jsse.provider.BouncyCastleJsseProvider JSSE 提供程序并将其与 org.bouncycastle.jcajce.provider.BouncyCastleFipsProvider 结合使用,而不是默认的 SunJSSE 提供程序,请设置一个 BCFIPSJSSE 提供程序名称

安全提供程序 BouncyCastle FIPS JSSE 配置示例
quarkus.security.security-providers=BCFIPSJSSE

quarkus.http.ssl.client-auth=REQUIRED

quarkus.http.ssl.certificate.key-store-file=server-keystore.jks
quarkus.http.ssl.certificate.key-store-password=password
quarkus.http.ssl.certificate.key-store-file-type=BCFKS
quarkus.http.ssl.certificate.key-store-provider=BCFIPS
quarkus.http.ssl.certificate.trust-store-file=server-truststore.jks
quarkus.http.ssl.certificate.trust-store-password=password
quarkus.http.ssl.certificate.trust-store-file-type=BCFKS
quarkus.http.ssl.certificate.trust-store-provider=BCFIPS

以及针对使用 BouncyCastle FIPS 提供程序进行了优化的 BouncyCastle TLS 依赖项

pom.xml
<dependency>
  <groupId>org.bouncycastle</groupId>
  <artifactId>bctls-fips</artifactId>
</dependency>

<dependency>
    <groupId>org.bouncycastle</groupId>
    <artifactId>bc-fips</artifactId>
</dependency>
build.gradle
implementation("org.bouncycastle:bctls-fips")
implementation("org.bouncycastle:bc-fips")

请注意,密钥库和信任库类型和提供程序已设置为 BCFKSBCFIPS。可以使用此类型和提供程序生成密钥库,如下所示

keytool -genkey -alias server -keyalg RSA -keystore server-keystore.jks -keysize 2048 -keypass password -provider org.bouncycastle.jcajce.provider.BouncyCastleFipsProvider -providerpath $PATH_TO_BC_FIPS_JAR -storetype BCFKS

本机映像目前不支持 BCFIPSJSSE 提供程序选项。

SunPKCS11

SunPKCS11 提供程序提供到特定 PKCS#11 实现的桥梁,例如加密智能卡和其他硬件安全模块、FIPS 模式下的网络安全服务等。

通常,为了使用 SunPKCS11,需要安装 PKCS#11 实现,生成一个通常引用共享库、令牌槽等的配置,并编写以下 Java 代码

import java.security.Provider;
import java.security.Security;

String configuration = "pkcs11.cfg"

Provider sunPkcs11 = Security.getProvider("SunPKCS11");
Provider pkcsImplementation = sunPkcs11.configure(configuration);
// or prepare configuration in the code or read it from the file such as "pkcs11.cfg" and do
// sunPkcs11.configure("--" + configuration);
Security.addProvider(pkcsImplementation);

在 Quarkus 中,您可以通过配置级别实现相同目标,而无需修改代码,例如

quarkus.security.security-providers=SunPKCS11
quarkus.security.security-provider-config.SunPKCS11=pkcs11.cfg

请注意,虽然本机映像支持访问 SunPKCS11 桥接提供程序,但 Quarkus 级别本机映像目前不支持配置 SunPKCS11

响应式安全

如果您打算在响应式环境中使用安全性,则可能需要 SmallRye 上下文传播

pom.xml
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-smallrye-context-propagation</artifactId>
</dependency>
build.gradle
implementation("io.quarkus:quarkus-smallrye-context-propagation")

这将允许您在整个响应式回调中传播身份。您还需要确保使用能够传播身份的执行程序(例如,没有 CompletableFuture.supplyAsync),以确保 Quarkus 可以传播身份。有关更多信息,请参见上下文传播指南

观察安全事件

Quarkus bean 可以使用 CDI 观察者 来使用身份验证和授权安全事件。观察者可以是同步的也可以是异步的。

支持的安全事件列表
  • io.quarkus.security.spi.runtime.AuthenticationFailureEvent

  • io.quarkus.security.spi.runtime.AuthenticationSuccessEvent

  • io.quarkus.security.spi.runtime.AuthorizationFailureEvent

  • io.quarkus.security.spi.runtime.AuthorizationSuccessEvent

  • io.quarkus.oidc.SecurityEvent

  • io.quarkus.vertx.http.runtime.security.FormAuthenticationEvent

有关特定于 Quarkus OpenID Connect 扩展的安全事件的更多信息,请参见保护 Web 应用程序指南的 OIDC 代码流机制的收听重要身份验证事件部分。
package org.acme.security;

import io.quarkus.security.spi.runtime.AuthenticationFailureEvent;
import io.quarkus.security.spi.runtime.AuthenticationSuccessEvent;
import io.quarkus.security.spi.runtime.AuthorizationFailureEvent;
import io.quarkus.security.spi.runtime.AuthorizationSuccessEvent;
import io.quarkus.security.spi.runtime.SecurityEvent;
import io.vertx.ext.web.RoutingContext;
import jakarta.enterprise.event.Observes;
import jakarta.enterprise.event.ObservesAsync;
import org.jboss.logging.Logger;

public class SecurityEventObserver {

    private static final Logger LOG = Logger.getLogger(SecurityEventObserver.class.getName());

    void observeAuthenticationSuccess(@ObservesAsync AuthenticationSuccessEvent event) {    (1)
        LOG.debugf("User '%s' has authenticated successfully", event.getSecurityIdentity().getPrincipal().getName());
    }

    void observeAuthenticationFailure(@ObservesAsync AuthenticationFailureEvent event) {
        RoutingContext routingContext = (RoutingContext) event.getEventProperties().get(RoutingContext.class.getName());
        LOG.debugf("Authentication failed, request path: '%s'", routingContext.request().path());
    }

    void observeAuthorizationSuccess(@ObservesAsync AuthorizationSuccessEvent event) {
        String principalName = getPrincipalName(event);
        if (principalName != null) {
            LOG.debugf("User '%s' has been authorized successfully", principalName);
        }
    }

    void observeAuthorizationFailure(@Observes AuthorizationFailureEvent event) {
        LOG.debugf(event.getAuthorizationFailure(), "User '%s' authorization failed", event.getSecurityIdentity().getPrincipal().getName());
    }

    private static String getPrincipalName(SecurityEvent event) {   (2)
        if (event.getSecurityIdentity() != null) {
            return event.getSecurityIdentity().getPrincipal().getName();
        }
        return null;
    }

}
1 此观察者异步使用所有 AuthenticationSuccessEvent 事件,这意味着 HTTP 请求处理将继续,而与事件处理无关。根据应用程序的不同,可能会有很多 AuthenticationSuccessEvent 事件。因此,异步处理可能对性能产生积极影响。
2 所有支持的安全事件类型都具有通用代码,因为它们都实现了 io.quarkus.security.spi.runtime.SecurityEvent 接口。

Vert.x 路由处理程序中的 SecurityIdentity 注入

当您需要在直接在 Vert.x Web Router 上注册的 Vert.x 路由处理程序中注入 SecurityIdentity 时,需要请求 SecurityIdentity 传播。例如

package io.quarkus.it.security;

public class RouterObserver {

    public void route(@Observes Router router, UserInformation userInformation) {
        router.route("/user-info").handler(event -> event.response().end(userInformation.getPrincipalName()));
    }

}

示例中的 UserInformation bean 可能如下所示

package io.quarkus.it.security;

@ApplicationScoped
public class UserInformation {

    @Inject
    SecurityIdentity identity;

    @ActivateRequestContext
    String getPrincipalName() {
        return identity.getPrincipal().getName();
    }

}

为了使此示例中的 SecurityIdentity 注入起作用,您必须激活 SecurityIdentity 传播

quarkus.http.auth.propagate-security-identity=true (1)
1 当以编程方式在 Vert.x HTTP 路由器上注册 Vert.x 路由处理程序时,传播 SecurityIdentity。这是必需的,因为 Quarkus 无法检测到 SecurityIdentity 注入点在以编程方式注册的 Vert.x 路由处理程序中使用。对于使用 @Route 注释声明性注册的路由以及 Jakarta REST 端点,不需要此配置。

相关内容