Quarkus响应式中ContainerRequestFilter发起HTTP请求的问题
在Quarkus响应式环境下的Filter中正确发起HTTP请求的方案
你的核心问题在于:普通ContainerRequestFilter的同步/异步处理方式无法适配Quarkus响应式模型,要么异步操作延迟导致SecurityContext设置不及时,要么阻塞操作违反事件循环线程的非阻塞要求。以下是两种可行的解决方案:
方案一:使用异步Filter接口(推荐,完全响应式)
实现AsyncContainerRequestFilter接口,通过返回CompletionStage让容器等待异步HTTP请求完成后再继续处理请求,从根本上避免异步回调延迟或线程阻塞问题。
@Startup @Provider @PreMatching public class SecurityOverrideFilter implements AsyncContainerRequestFilter { @Inject Logger logger; @Inject @RestClient SsoClient ssoClient; @Override public CompletionStage<Void> filter(ContainerRequestContext requestContext) { String authHeader = requestContext.getHeaders().getFirst("Authorization"); // 处理无认证头的情况 if (authHeader == null) { requestContext.abortWith(Response.status(Response.Status.UNAUTHORIZED).build()); return CompletableFuture.completedFuture(null); } // 异步调用SSO服务,完成后设置SecurityContext return ssoClient.profile(authHeader) .onItem().invoke(profile -> { requestContext.setSecurityContext(new SecurityContext() { @Override public Principal getUserPrincipal() { return () -> profile.getName(); } @Override public boolean isUserInRole(String role) { return true; // 根据实际业务逻辑调整 } @Override public boolean isSecure() { // 根据请求实际协议判断是否安全 return "https".equals(requestContext.getUriInfo().getRequestUri().getScheme()); } @Override public String getAuthenticationScheme() { return "Bearer"; // 根据实际认证方式调整 } }); }) .onFailure().invoke(failure -> { logger.error("获取用户信息失败", failure); requestContext.abortWith(Response.status(Response.Status.UNAUTHORIZED).build()); }) .subscribeAsCompletionStage(); } }
为什么这个方案可行?
AsyncContainerRequestFilter的filter方法返回CompletionStage,Quarkus会等待这个异步任务完成后再执行后续的请求处理流程,确保SecurityContext在资源方法执行前已设置完毕。- 全程基于Mutiny响应式API操作,不会阻塞事件循环线程,符合Quarkus响应式模型的设计要求。
方案二:使用虚拟线程(Quarkus 3.2+支持)
如果你更倾向于同步代码写法,可以升级到Quarkus 3.2及以上版本,使用@RunOnVirtualThread注解让Filter在虚拟线程中执行,此时调用await()不会阻塞事件循环线程。
@Startup @Provider @PreMatching public class SecurityOverrideFilter implements ContainerRequestFilter { @Inject Logger logger; @Inject @RestClient SsoClient ssoClient; @Override @RunOnVirtualThread public void filter(ContainerRequestContext requestContext) { String authHeader = requestContext.getHeaders().getFirst("Authorization"); if (authHeader == null) { requestContext.abortWith(Response.status(Response.Status.UNAUTHORIZED).build()); return; } try { // 阻塞等待SSO请求完成,但虚拟线程不会占用事件循环线程 Profile profile = ssoClient.profile(authHeader) .await().atMost(Duration.of(10, ChronoUnit.SECONDS)); requestContext.setSecurityContext(new SecurityContext() { // 实现逻辑同方案一 @Override public Principal getUserPrincipal() { return () -> profile.getName(); } @Override public boolean isUserInRole(String role) { return true; } @Override public boolean isSecure() { return "https".equals(requestContext.getUriInfo().getRequestUri().getScheme()); } @Override public String getAuthenticationScheme() { return "Bearer"; } }); } catch (Exception e) { logger.error("获取用户信息失败", e); requestContext.abortWith(Response.status(Response.Status.UNAUTHORIZED).build()); } } }
为什么之前的@Blocking注解无效?
@Blocking注解在普通ContainerRequestFilter上可能无法正确切换线程池,导致代码仍然运行在事件循环线程中,而Quarkus严格禁止在事件循环线程上执行阻塞操作(比如await()),因此抛出IllegalStateException。虚拟线程则是专门为这类阻塞场景设计的轻量级线程,不会占用事件循环资源。
内容的提问来源于stack exchange,提问作者tiagomistral
相关产品推荐
相关产品推荐

