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

Quarkus GraphQL JWT认证自定义与默认配置异常排查

Quarkus GraphQL JWT 认证扩展与问题解决

一、核心问题逐一解答

1. @Priority 注解选择

必须用 io.quarkus.arc.Priority。Quarkus的Arc容器优先识别自身的Priority注解,javax.annotation.Priority是Java EE标准注解,在Quarkus环境中优先级低于Arc实现,用它无法确保自定义认证机制覆盖默认的JWT认证逻辑。

2. authenticate 方法未被调用的原因

  • 你返回的getCredentialTypes()是TokenAuthenticationRequest.class,但默认JWT认证机制已经处理了该类型请求,且优先级可能未被你的机制覆盖(虽然你设了@Priority(1),但需确认是否有其他更高优先级的认证Bean存在)。
  • 若要直接处理JWT,需手动从请求头提取Token并解析,而不是直接返回固定用户。另外要确保GraphQL的/graphql端点被Quarkus安全过滤器拦截(默认会拦截,但若有自定义路径排除配置需检查)。

3. getChallenge 返回值优化

你的写法没问题,但可以用Quarkus提供的静态常量简化:

return Uni.createFrom().item(ChallengeData.UNAUTHORIZED);

它会自动设置符合JWT规范的WWW-Authenticate: Bearer挑战头,比手动构建更规范。

4. getCredentialTypes 返回值

如果专门处理JWT令牌,返回TokenAuthenticationRequest.class是正确的;如果想让机制兼容所有认证请求类型,可以返回Collections.singleton(AuthenticationRequest.class)。

二、默认配置下 UnauthorizedException 问题排查

用smallrye-jwt默认配置仍报错,大概率是以下原因:

  • 密钥不匹配:检查jwtKey.json是否为有效JWK格式,签名用的密钥和验证用的密钥必须一致(对称密钥要完全相同,非对称需用私钥签名、公钥验证)。
  • JWT声明缺失:smallrye-jwt默认要求aud(受众)声明,若你的JWT没有该字段,需在配置中添加:
    smallrye.jwt.verify.issuer=my_site
    smallrye.jwt.audience=liquido-api # 可设为*跳过验证,但不推荐
    
  • Token格式错误:请求头的Authorization必须是Bearer <token>格式,不能遗漏Bearer前缀。
  • 端点未被拦截:确认/graphql路径未被排除在Quarkus安全拦截范围外(默认会拦截所有端点,无需额外配置)。

三、自定义JWT认证机制示例(扩展角色)

以下是能正确解析JWT并添加自定义角色的实现代码:

@Alternative
@Priority(1)
@ApplicationScoped
public class CustomJwtAuthMechanism implements HttpAuthenticationMechanism {

    @Override
    public Uni<SecurityIdentity> authenticate(RoutingContext context, IdentityProviderManager identityProviderManager) {
        String authHeader = context.request().getHeader(HttpHeaderNames.AUTHORIZATION);
        if (authHeader == null || !authHeader.startsWith("Bearer ")) {
            return Uni.createFrom().nullItem(); // 无Token,触发挑战
        }

        String token = authHeader.substring(7);
        try {
            JsonWebToken parsedJwt = Jwt.decode(token);
            Set<String> roles = new HashSet<>(parsedJwt.getGroups());
            roles.add("CUSTOM_USER"); // 添加自定义角色

            QuarkusSecurityIdentity identity = QuarkusSecurityIdentity.builder()
                    .setPrincipal(parsedJwt)
                    .addRoles(roles)
                    .build();

            return Uni.createFrom().item(identity);
        } catch (Exception e) {
            return Uni.createFrom().failure(new UnauthorizedException("无效的JWT令牌"));
        }
    }

    @Override
    public Uni<ChallengeData> getChallenge(RoutingContext context) {
        return Uni.createFrom().item(ChallengeData.UNAUTHORIZED);
    }

    @Override
    public Set<Class<? extends AuthenticationRequest>> getCredentialTypes() {
        return Collections.singleton(TokenAuthenticationRequest.class);
    }
}

四、正确的JWT生成与配置示例

application.properties 配置

smallrye.jwt.sign.key.location=META-INF/jwtKey.json
smallrye.jwt.verify.key.location=META-INF/jwtKey.json
smallrye.jwt.verify.issuer=my_site
smallrye.jwt.audience=liquido-api

JWT生成代码(包含必要声明)

String jwtToken = Jwt.issuer("my_site")
        .subject("TestUser11@liquido.vote")
        .upn("upn@liquido.vote")
        .audience("liquido-api")
        .groups(Collections.singleton("LIQUIDO_USER"))
        .expiresAt(ZonedDateTime.now().plusHours(1)) // 添加过期时间
        .sign();

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 19:05:44