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

Quarkus中自定义ParamConverterProvider参数注入失效问题求助

问题分析与解决方案

核心问题原因

  1. ParamConverterProvider 适用场景错误:JAX-RS 的 ParamConverterProvider 仅负责转换已从请求中获取的参数值(比如@QueryParam/@PathParam的字符串转目标类型),而非主动注入请求中不存在的参数。你的自定义注解@KeycloakRole未绑定任何标准参数注解,JAX-RS 运行时不会触发该转换器。
  2. Token 角色解析逻辑错误:Keycloak AccessToken 的resource_access字段是JsonObject(键为客户端ID,值为包含角色的对象),而非你代码中假设的JsonArray,这会导致角色提取失败。
  3. SecurityContext 类型转换错误:直接将SecurityContext强转为KeycloakSecurityContext是错误的,正确方式是从SecurityContext.getUserPrincipal()获取实例。

修正方案

1. 修改自定义注解,添加 CDI Qualifier 标识

CDI 需要通过 Qualifier 区分不同的注入类型,给@KeycloakRole添加@Qualifier注解:

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.PARAMETER, ElementType.FIELD})
@Qualifier
public @interface KeycloakRole {
    String realm();
    String role();
}

2. 实现 CDI 生产者,负责角色注入与校验

用 CDI 生产者替代 ParamConverter,主动提取并校验用户角色:

@RequestScoped
public class KeycloakRoleProducer {

    @Context
    SecurityContext securityContext;

    @Produces
    @KeycloakRole(realm = "", role = "")
    public Set<String> produceKeycloakRole(InjectionPoint injectionPoint) {
        // 获取方法参数上的注解配置
        KeycloakRole annotation = injectionPoint.getAnnotated().getAnnotation(KeycloakRole.class);
        String requiredRole = annotation.role();

        Set<String> userRoles = extractUserRoles();

        // 校验用户是否拥有要求的角色
        if (!userRoles.contains(requiredRole)) {
            throw new WebApplicationException("User does not have required role", Response.Status.FORBIDDEN);
        }

        return userRoles;
    }

    private Set<String> extractUserRoles() {
        // 从SecurityContext获取Keycloak上下文
        KeycloakSecurityContext keycloakCtx = (KeycloakSecurityContext) securityContext.getUserPrincipal();
        if (keycloakCtx == null) {
            throw new WebApplicationException("Unauthorized", Response.Status.UNAUTHORIZED);
        }

        AccessToken token = keycloakCtx.getToken();
        if (token == null) {
            throw new WebApplicationException("Failed to access user token", Response.Status.UNAUTHORIZED);
        }

        // 正确解析resource_access结构
        Map<String, Object> resourceAccess = token.getResourceAccess();
        if (resourceAccess == null) {
            return Collections.emptySet();
        }

        // 替换为你的实际客户端ID
        Object clientAccess = resourceAccess.get("profiler-core");
        if (!(clientAccess instanceof JsonObject)) {
            return Collections.emptySet();
        }

        JsonArray rolesArray = ((JsonObject) clientAccess).getJsonArray("roles");
        if (rolesArray == null) {
            return Collections.emptySet();
        }

        Set<String> roles = new HashSet<>();
        for (int i = 0; i < rolesArray.size(); i++) {
            roles.add(rolesArray.getString(i));
        }
        return roles;
    }
}

3. 简化端点方法,直接注入角色

修改后的端点无需额外配置,CDI 会自动处理注入与校验:

@Path("/api/profile")
@Authenticated
@ApplicationScoped
public class AdminResource {

    @GET
    @Path("/custom-role-ep")
    public Collection<String> customEp(@KeycloakRole(realm = "profiler", role = "view-all") Set<String> userRoles) {
        LOG.info("User roles: " + userRoles);
        return userRoles;
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 12:52:07