第 4 章 主动验证
了解如何在 Quarkus 中管理主动身份验证,包括自定义设置和处理异常。获取各种应用程序场景的实用见解和策略。
主动验证在 Quarkus 中默认启用。它确保所有传入的请求都通过身份验证,即使目标页面不需要身份验证。因此,带有无效凭证的请求将被拒绝,即使目标页面是公共的。
只有在目标页面需要时才需要身份验证时,可以关闭此默认行为。要关闭主动身份验证,以便仅在目标页面需要时才进行身份验证,请按如下所示修改 application.properties
配置文件:
quarkus.http.auth.proactive=false
如果您关闭主动身份验证,身份验证过程仅在请求身份时运行。可以请求一个身份,因为需要需要用户进行身份验证的安全规则,或者因为需要对当前身份进行编程访问。
如果使用主动身份验证,访问 SecurityIdentity
是一个阻止操作。这是因为身份验证可能已经发生,访问 SecurityIdentity
可能需要调用外部系统,如数据库,这可能会阻止操作。对于阻止应用程序,这并不是一个问题。但是,如果您在被动应用程序中禁用了身份验证,则会失败,因为您无法阻止 I/O 线程的操作。要临时解决这个问题,您需要 @Inject
一个 io.quarkus.security.identity.CurrentIdentityAssociation
实例,并调用 Uni<SecurityIdentity> getDeferredIdentity ();
方法。然后,您可以订阅生成的 Uni
,以便在身份验证完成后获得通知,且身份可用。
您仍然可以从使用 @RolesAllowed
、@Authenticated、
如果路由响应是同步,则也对 Reactive 路由 也有效。
@Authenticated
,或具有相应配置授权检查的端点同时访问 Quarkus REST 中的 SecurityIdentity
getIdentity () (以前为 RESTEasy Reactive)。
禁用主动身份验证后,如果未同步返回值的安全方法,则 CDI Bean 上使用 的标准安全注解 在 I/O 线程上无法正常工作。由于需要这些方法访问 SecurityIdentity
,就会产生这个限制。
以下示例定义了 HelloResource
和 HelloService
。任何对 /hello
的 GET 请求都在 I/O 线程上运行,并抛出 BlockingOperationNotAllowedException
异常。
修复示例的方法不止一种:
-
通过注解带有
@Blocking
的hello
端点来切换到 worker 线程。 -
使用被动或异步数据类型更改
sayHello
方法返回类型。 -
将
@RolesAllowed
注释移到端点。这可能是安全的方法之一,因为从端点方法访问SecurityIdentity
并不是阻塞操作。
import jakarta.annotation.security.PermitAll; import jakarta.inject.Inject; import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; import io.smallrye.mutiny.Uni; @Path("/hello") @PermitAll public class HelloResource { @Inject HelloService helloService; @GET public Uni<String> hello() { return Uni.createFrom().item(helloService.sayHello()); } }
import jakarta.annotation.security.RolesAllowed; import jakarta.enterprise.context.ApplicationScoped; @ApplicationScoped public class HelloService { @RolesAllowed("admin") public String sayHello() { return "Hello"; } }
4.1. 自定义身份验证异常响应
您可以使用 Jakarta REST ExceptionMapper
来捕获 Quarkus 安全身份验证异常,如 io.quarkus.security.AuthenticationFailedException
。例如:
package io.quarkus.it.keycloak; import jakarta.annotation.Priority; import jakarta.ws.rs.Priorities; import jakarta.ws.rs.core.Context; import jakarta.ws.rs.core.Response; import jakarta.ws.rs.core.UriInfo; import jakarta.ws.rs.ext.ExceptionMapper; import jakarta.ws.rs.ext.Provider; import io.quarkus.security.AuthenticationFailedException; @Provider @Priority(Priorities.AUTHENTICATION) public class AuthenticationFailedExceptionMapper implements ExceptionMapper<AuthenticationFailedException> { @Context UriInfo uriInfo; @Override public Response toResponse(AuthenticationFailedException exception) { return Response.status(401).header("WWW-Authenticate", "Basic realm=\"Quarkus\"").build(); } }
有些 HTTP 身份验证机制必须处理身份验证异常本身,才能创建正确的身份验证问题。例如: io.quarkus.oidc.runtime.CodeAuthenticationMechanism
,它管理 OpenID Connect (OIDC)授权代码流身份验证,必须构建正确的重定向 URL 并设置状态 Cookie。因此,避免使用自定义异常映射器自定义由此类机制引发的身份验证异常。相反,一种更安全的方法是确保启用主动身份验证并使用 Vert.x HTTP 路由失败处理程序。这是因为事件发送到带有正确响应状态和标头的处理程序。然后,您必须只自定义响应,例如:
package io.quarkus.it.keycloak; import jakarta.enterprise.context.ApplicationScoped; import jakarta.enterprise.event.Observes; import io.quarkus.security.AuthenticationFailedException; import io.vertx.core.Handler; import io.vertx.ext.web.Router; import io.vertx.ext.web.RoutingContext; @ApplicationScoped public class AuthenticationFailedExceptionHandler { public void init(@Observes Router router) { router.route().failureHandler(new Handler<RoutingContext>() { @Override public void handle(RoutingContext event) { if (event.failure() instanceof AuthenticationFailedException) { event.response().end("CUSTOMIZED_RESPONSE"); } else { event.next(); } } }); } }