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

如何结合Spring Security实现DGS Subscriptions的认证与授权?

解决DGS框架订阅中connection_init消息的认证问题

核心思路

DGS框架默认不会自动处理WebSocket连接初始化阶段(connection_init)的Authorization信息,需要自定义拦截逻辑提取token并初始化Authentication对象,同时让该对象能在后续订阅方法中被Spring Security识别。

具体实现步骤

1. 自定义WebSocket拦截器提取connection_init中的token

创建实现WebSocketHandlerDecoratorFactory的类,拦截connection_init消息,解析Authorization头(通常为Bearer token),完成认证后将Authentication存入Reactor上下文:

@Component
public class SubscriptionAuthInterceptor implements WebSocketHandlerDecoratorFactory {
    private final AuthenticationManager authenticationManager;

    public SubscriptionAuthInterceptor(AuthenticationManager authenticationManager) {
        this.authenticationManager = authenticationManager;
    }

    @Override
    public WebSocketHandler decorate(WebSocketHandler handler) {
        return new WebSocketHandler() {
            @Override
            public Mono<Void> handle(WebSocketSession session) {
                return session.receive()
                    .flatMap(message -> {
                        if (message.getType() == WebSocketMessage.Type.TEXT) {
                            String payload = message.getPayloadAsText();
                            JsonNode jsonNode = new ObjectMapper().readTree(payload);
                            if ("connection_init".equals(jsonNode.get("type").asText())) {
                                JsonNode authNode = jsonNode.get("payload").get("Authorization");
                                if (authNode != null) {
                                    String token = authNode.asText().replace("Bearer ", "");
                                    UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(null, token);
                                    return Mono.fromCallable(() -> authenticationManager.authenticate(authRequest))
                                        .flatMap(authentication -> 
                                            handler.handle(session)
                                                .contextWrite(Context.of(Authentication.class, authentication))
                                        );
                                }
                            }
                        }
                        return handler.handle(session);
                    });
            }
        };
    }
}

2. 配置SecurityWebFilterChain允许订阅端点的WebSocket握手

无需完全放开/subscriptions,仅允许WebSocket握手请求,后续消息层面由自定义拦截器处理:

@Bean
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
    return http
        .authorizeExchange(exchanges -> exchanges
            .pathMatchers(HttpMethod.GET, "/subscriptions").permitAll()
            .anyExchange().authenticated()
        )
        .csrf(ServerHttpSecurity.CsrfSpec::disable)
        .build();
}

3. 让Spring Security识别Reactor上下文的Authentication

自定义ReactiveAuthenticationManager实现,从Reactor上下文中读取已认证的Authentication:

@Component
public class ReactorContextAuthenticationManager implements ReactiveAuthenticationManager {
    @Override
    public Mono<Authentication> authenticate(Authentication authentication) {
        return Mono.deferContextual(contextView -> {
            if (contextView.hasKey(Authentication.class)) {
                return Mono.just(contextView.get(Authentication.class));
            }
            return Mono.empty();
        });
    }
}

4. 在订阅方法上使用@PreAuthorize

此时@PreAuthorize注解可正确识别Authentication对象:

@DgsSubscription
@PreAuthorize("hasAuthority('read')")
public Flux<Entity> subscribeToEntity(@Input String id) {
    // 业务订阅逻辑
}

补充说明

  • 若使用JWT,需替换AuthenticationManager为JwtAuthenticationManager,适配token解析逻辑
  • 确保前端传递的connection_init payload格式正确,Authorization字段符合约定
  • 该方案支持从connection_init消息中提取认证信息,无需依赖握手阶段的请求头

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 07:11:22