【问题标题】:GraphQL Subscriptions - Spring Boot Websocket AuthenticationGraphQL 订阅 - Spring Boot Websocket 身份验证
【发布时间】:2022-10-12 23:42:36
【问题描述】:
我们正在使用Netflix DGS 框架来构建我们的后端以提供 GraphQL API。
除此之外,我们使用Keykloak 作为身份提供者,它带有一个方便的 Spring 模块,以添加对开箱即用的身份验证和授权的支持。
每个请求都包含一个 JWT 令牌,该令牌得到验证,并从那里生成一个 SecurityContext 对象,然后在每个端点中都可以使用该对象。
这对 HTTP 请求非常有用。 GraphQL 查询和突变是通过 HTTP 发送的,因此这里没有问题。
另一方面,订阅使用 Web 套接字协议。 WS 请求不包含额外的标头,因此不会随请求一起发送 JWT 令牌。
我们可以通过有效负载添加令牌,现在的问题是如何设置一个 Spring Security Filter,它从有效负载中创建一个安全上下文。
我想这是相当 Spring 特定的,基本上需要一个拦截任何 Web 套接字请求(ws://... 或 wss://...)的过滤器。
非常感谢任何帮助或提示!
【问题讨论】:
标签:
spring
authentication
spring-security
websocket
graphql
【解决方案1】:
在 web 套接字消息中使用标头的唯一方法是在 connection_init 消息中。标头将由客户端在消息的有效负载中发送。
我提出的解决方案分两步完成(我们假设标题元素的名称是“token”):
- 拦截 connection_init 消息,然后强制在订阅请求中插入新元素(令牌)。
- 在订阅拦截期间检索标头的元素(令牌)并提供上下文。
具体解决方案是WebSocketGraphQlInterceptor接口的实现
@Configuration
class SubscriptionInterceptor implements WebSocketGraphQlInterceptor {
@Override
public Mono<Object> handleConnectionInitialization(WebSocketSessionInfo sessionInfo, Map<String, Object> connectionInitPayload) {
sessionInfo.getHeaders().add("token", connectionInitPayload.get("token").toString());
return Mono.just(connectionInitPayload);
}
@Override
public Mono<WebGraphQlResponse> intercept(WebGraphQlRequest request, Chain chain) {
List<String> token = request.getHeaders().getOrEmpty("token");
return chain.next(request).contextWrite(context -> context. Put("token", token.isEmpty() ? "" : token.get(0)));
}
}