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

Quarkus中GraphQL订阅接口的身份认证问题(graphql-ui)

解决Quarkus + SmallRye GraphQL订阅接口的身份认证问题

问题场景

你在Quarkus中使用SmallRye GraphQL实现了包含查询和订阅的接口,查询接口通过添加Authorization: Bearer <token>请求头可正常完成认证,但调用订阅接口时即便使用相同请求头配置,仍返回unauthorized错误。

核心原因

GraphQL订阅基于WebSocket协议,其认证流程与HTTP查询存在差异:订阅的初始连接依赖HTTP握手请求建立,但Quarkus默认安全配置可能未覆盖WebSocket握手阶段的认证逻辑,导致令牌无法被正确验证;部分GraphQL UI工具也可能未将HTTP请求头传递到WebSocket握手请求中。

解决方案

1. 配置Quarkus安全规则覆盖GraphQL WebSocket路径

在application.properties中添加安全规则,确保/graphql路径的WebSocket握手请求触发认证流程:

# 匹配GraphQL的WebSocket握手路径
quarkus.http.auth.permission.graphql-websocket.paths=/graphql
# 要求该路径的请求必须经过认证
quarkus.http.auth.permission.graphql-websocket.policy=authenticated

2. 手动处理WebSocket握手阶段的令牌验证

如果默认安全过滤器未处理WebSocket请求,可创建一个请求拦截器,在握手阶段提取并验证令牌:

import jakarta.ws.rs.container.ContainerRequestContext;
import jakarta.ws.rs.container.ContainerRequestFilter;
import jakarta.ws.rs.core.HttpHeaders;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.ext.Provider;

@Provider
public class WebSocketAuthFilter implements ContainerRequestFilter {

    @Override
    public void filter(ContainerRequestContext requestContext) {
        // 判断是否为WebSocket握手请求
        String upgradeHeader = requestContext.getHeaderString("Upgrade");
        if (upgradeHeader != null && upgradeHeader.equalsIgnoreCase("websocket")) {
            String authHeader = requestContext.getHeaderString(HttpHeaders.AUTHORIZATION);
            // 验证Bearer令牌格式
            if (authHeader == null || !authHeader.startsWith("Bearer ")) {
                requestContext.abortWith(Response.status(Response.Status.UNAUTHORIZED).build());
                return;
            }
            String token = authHeader.substring(7);
            // 集成Quarkus的JWT验证逻辑,示例:若验证失败则终止请求
            // if (!isValidToken(token)) {
            //     requestContext.abortWith(Response.status(Response.Status.UNAUTHORIZED).build());
            // }
        }
    }

    // 自定义令牌验证方法
    // private boolean isValidToken(String token) {
    //     // 实现你的JWT验证逻辑
    // }
}

3. 确保订阅方法能获取认证上下文

在订阅类中注入SecurityContext,手动验证当前会话的认证状态:

import jakarta.inject.Inject;
import jakarta.ws.rs.core.SecurityContext;
import org.eclipse.microprofile.graphql.GraphQLApi;
import org.eclipse.microprofile.graphql.Query;
import io.quarkus.security.Authenticated;
import io.smallrye.graphql.api.Subscription;
import java.util.List;
import org.reactivestreams.Publisher;

@GraphQLApi
@Authenticated
public class MyGraphQL {

    @Inject
    SecurityContext securityContext;

    @Query("getInformation")
    public List<MyInformation> getMyInformation(int id) {
        // 查询业务逻辑
        return null;
    }

    @Subscription("informationStream")
    public Publisher<MyInformation> getMyInformationStream() {
        // 验证当前用户是否已认证
        if (securityContext.getUserPrincipal() == null) {
            throw new SecurityException("未授权访问订阅接口");
        }
        // 订阅业务逻辑
        return null;
    }
}

4. 验证GraphQL UI的WebSocket请求头配置

部分版本的Quarkus GraphQL UI(graphql-ui)不会自动将HTTP请求头传递到WebSocket握手请求中,你可以:

  • 在GraphQL UI的"Headers"设置中手动添加Authorization头;
  • 使用Postman、GraphQL Playground等支持WebSocket请求头的工具测试订阅接口。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 10:08:29