You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 06:55:39