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

Quarkus/Vert.x HttpAuthenticationMechanism获取ResourceInfo方法

Quarkus RESTEasy Reactive 中 HttpAuthenticationMechanism 获取ResourceInfo实现方案

以下方案可直接解决RESTEasy Reactive下HttpAuthenticationMechanism无法获取ResourceInfo的问题,相关问题本质是请求处理链路顺序、Bean作用域不匹配导致的,不需要绕路加过滤器:

核心实现逻辑

RESTEasy Reactive的路由匹配动作在安全认证拦截执行前已经完成,匹配到的资源类、方法元数据会直接挂载到当前请求的RoutingContext属性中,直接在authenticate方法内读取即可,完全不需要依赖请求作用域注入、不需要等待ContainerRequestFilter执行。

代码实现

import io.quarkus.security.identity.IdentityProviderManager;
import io.quarkus.security.identity.SecurityIdentity;
import io.quarkus.vertx.http.runtime.security.HttpAuthenticationMechanism;
import io.smallrye.mutiny.Uni;
import io.vertx.ext.web.RoutingContext;
import jakarta.annotation.Priority;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.ws.rs.Priorities;
import org.jboss.resteasy.reactive.server.core.ResteasyReactiveRequestContext;
import org.jboss.resteasy.reactive.server.jaxrs.ResourceInfoImpl;
import jakarta.ws.rs.container.ResourceInfo;
import java.lang.reflect.Method;
import java.util.Optional;

@ApplicationScoped
@Priority(Priorities.AUTHENTICATION)
public class RoutingAuthMechanism implements HttpAuthenticationMechanism {

    final AuthConfigMapping authConfigMapping;

    public RoutingAuthMechanism(AuthConfigMapping authConfigMapping) {
        this.authConfigMapping = authConfigMapping;
    }

    @Override
    public Uni<SecurityIdentity> authenticate(RoutingContext context, IdentityProviderManager identityProviderManager) {
        // 从当前请求上下文取出RESTEasy Reactive提前完成匹配的资源元信息
        ResteasyReactiveRequestContext rrContext = context.get(ResteasyReactiveRequestContext.class.getName());
        // 未匹配到JAX-RS资源的静态请求、404请求,直接返回空走默认逻辑
        if (rrContext == null) {
            return Uni.createFrom().optional(Optional.empty());
        }

        // 构造标准ResourceInfo实例
        Class<?> resourceClass = rrContext.getTarget().getResourceClass();
        Method resourceMethod = rrContext.getTarget().getJavaMethod();
        ResourceInfo resourceInfo = new ResourceInfoImpl(resourceClass, resourceMethod);

        // 读取@Authorization注解,优先取方法上的注解,不存在则取类上的注解
        Authorization authAnnotation = resourceInfo.getResourceMethod().getAnnotation(Authorization.class);
        if (authAnnotation == null) {
            authAnnotation = resourceInfo.getResourceClass().getAnnotation(Authorization.class);
        }
        // 未标注注解的资源走默认认证逻辑
        if (authAnnotation == null) {
            return Uni.createFrom().optional(Optional.empty());
        }

        String authKey = authAnnotation.value();
        AuthConfigMapping.AuthConfig authConfig = authConfigMapping.auth().get(authKey);
        if (authConfig == null) {
            throw new RuntimeException("未找到认证配置:" + authKey);
        }

        // 根据authConfig.type()判断认证类型,分别调用JWT、OIDC等对应认证逻辑即可
        return switch (authConfig.type()) {
            case "jwt" -> doJwtAuth(context, identityProviderManager, authConfig);
            case "oidc" -> doOidcAuth(context, identityProviderManager, authConfig);
            default -> Uni.createFrom().optional(Optional.empty());
        };
    }

    private Uni<SecurityIdentity> doJwtAuth(RoutingContext context, IdentityProviderManager manager, AuthConfigMapping.AuthConfig config) {
        // 实现JWT认证逻辑
        return Uni.createFrom().optional(Optional.empty());
    }

    private Uni<SecurityIdentity> doOidcAuth(RoutingContext context, IdentityProviderManager manager, AuthConfigMapping.AuthConfig config) {
        // 实现OIDC认证逻辑
        return Uni.createFrom().optional(Optional.empty());
    }
}

相关配套代码

自定义Authorization注解

import java.lang.annotation.*;

@Inherited
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Authorization {
    // 对应配置中的认证项key
    String value();
}

注解支持同时标注在类、方法上,方法上的注解优先级高于类上的。

配置映射类

直接映射配置文件中myframework.auth下的所有配置项,不需要手动逐行读取配置:

import io.smallrye.config.ConfigMapping;
import java.util.Map;
import java.util.Optional;

@ConfigMapping(prefix = "myframework")
public interface AuthConfigMapping {
    Map<String, AuthConfig> auth();

    interface AuthConfig {
        String type();
        Optional<String> issuer();
        Optional<String> audience();
        Optional<String> authServerUrl();
    }
}

常见失效方案的原因说明

  • 直接注入ResourceInfo失效:ResourceInfo是请求作用域对象,HttpAuthenticationMechanism是应用作用域单例Bean,直接注入会拿到未绑定当前请求上下文的代理对象,调用时会抛出上下文不存在的异常。
  • 从RoutingContext未找到资源信息:之前查找的属性键不适用于RESTEasy Reactive,RESTEasy Reactive的请求上下文固定以类全限定名作为key存在RoutingContext中。
  • ContainerRequestFilter执行时机晚于认证:RESTEasy Reactive默认的请求过滤器优先级低于安全拦截优先级,即使给过滤器调整优先级提前执行,也只是多做一次上下文属性传递,不如直接在认证逻辑中读取元数据简洁。

该方案兼容Quarkus 2.13+、3.x全系列使用RESTEasy Reactive的版本,不存在执行时机、作用域不匹配问题。

内容的提问来源于stack exchange,提问作者Sof42lxca

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 03:33:23