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

Spring WebSocket配置报错:如何正确调用WebSocketSession?

问题分析与解决方案

关键错误根源

  1. WebSocketSession不能作为Spring Bean注入:它是每个WebSocket连接的临时实例,由Spring在连接建立时动态创建,并非容器管理的单例Bean,因此不能通过构造注入到配置类或拦截器中。
  2. 手动调用WebSocketHandler方法错误:Spring会自动管理WebSocketHandler的生命周期,连接建立时会自动触发afterConnectionEstablished方法,无需手动调用。
  3. STOMP与原生WebSocket配置冲突:你同时使用了@EnableWebSocketMessageBroker(STOMP协议)和自定义WebSocketHandler,两者的消息处理机制不一致,导致逻辑混乱。

方案一:基于STOMP协议的正确配置(推荐)

如果你需要使用STOMP的消息代理能力,按以下步骤修改:

1. 修改WebsocketConfig

移除WebSocketSession注入,改用Spring管理的拦截器:

@Configuration
@EnableWebSocketMessageBroker
@RequiredArgsConstructor
@Slf4j
public class WebsocketConfig implements WebSocketMessageBrokerConfigurer {

    private final HandshakeInterceptor handshakeInterceptor;

    @Override
    public void configureMessageBroker(MessageBrokerRegistry config) {
        config.enableSimpleBroker("/queue/", "/topic");
        config.setApplicationDestinationPrefixes("/app");
    }

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/connect-socket")
                .addInterceptors(handshakeInterceptor)
                .setAllowedOrigins("*");
        registry.addEndpoint("/connect-socket")
                .addInterceptors(handshakeInterceptor)
                .setAllowedOrigins("*")
                .withSockJS();
    }
}

2. 修改HandshakeInterceptor

移除WebSocketSession注入,在握手阶段完成参数验证:

@Component
@Slf4j
public class HandshakeInterceptor extends HttpSessionHandshakeInterceptor {

    @Override
    public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler,
                                   Map<String, Object> attributes) throws Exception {
        URI uri = request.getURI();
        String query = uri.getQuery();
        
        // 参数合法性校验
        if (query == null || !query.contains("auth=") || !query.contains("serialNumber=")) {
            response.setStatusCode(HttpStatus.BAD_REQUEST);
            return false;
        }

        // 解析参数(修复原代码的截取错误)
        String auth = query.substring(0, query.indexOf("&")).replace("auth=", "");
        String serialNumber = query.substring(query.lastIndexOf("=") + 1);

        // 执行连接验证逻辑
        if (!validateConnection(auth, serialNumber)) {
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            return false;
        }

        // 将验证后的参数存入属性,后续STOMP控制器可获取
        attributes.put("auth", auth);
        attributes.put("serialNumber", serialNumber);

        return super.beforeHandshake(request, response, wsHandler, attributes);
    }

    @Override
    public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler,
                               Exception ex) {
        super.afterHandshake(request, response, wsHandler, ex);
    }

    private boolean validateConnection(String auth, String serialNumber) {
        // 实现你的验证逻辑(比如调用权限服务)
        log.info("验证连接:auth={}, serialNumber={}", auth, serialNumber);
        return true; // 根据实际情况返回
    }
}

3. 移除自定义WebsocketHandler(STOMP模式下无需)

如果需要处理STOMP消息,创建控制器即可:

@Controller
public class StompController {

    @MessageMapping("/hello")
    @SendTo("/topic/greetings")
    public Greeting greeting(HelloMessage message, @Header("simpSessionAttributes") Map<String, Object> attributes) {
        String auth = (String) attributes.get("auth");
        String serialNumber = (String) attributes.get("serialNumber");
        log.info("收到消息:{},来自auth={}, serialNumber={}", message.getName(), auth, serialNumber);
        return new Greeting("Hello, " + message.getName() + "!");
    }
}

方案二:基于原生WebSocket的配置(无需STOMP)

如果不需要STOMP协议,直接使用原生WebSocket:

1. 修改WebsocketConfig

实现WebSocketConfigurer,注册自定义处理器:

@Configuration
@RequiredArgsConstructor
@Slf4j
public class WebsocketConfig implements WebSocketConfigurer {

    private final WebsocketHandler websocketHandler;
    private final HandshakeInterceptor handshakeInterceptor;

    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        registry.addHandler(websocketHandler, "/connect-socket")
                .addInterceptors(handshakeInterceptor)
                .setAllowedOrigins("*");
        // 支持SockJS
        registry.addHandler(websocketHandler, "/connect-socket")
                .addInterceptors(handshakeInterceptor)
                .setAllowedOrigins("*")
                .withSockJS();
    }
}

2. 修改HandshakeInterceptor(同方案一的拦截器代码)

移除WebSocketSession注入,仅做前置验证。

3. 保留并优化WebsocketHandler

Spring会自动触发生命周期方法:

@Component
@Slf4j
public class WebsocketHandler implements WebSocketHandler {

    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        Map<String, Object> attributes = session.getAttributes();
        String auth = (String) attributes.get("auth");
        String serialNumber = (String) attributes.get("serialNumber");
        
        log.info("============= 连接建立 =============");
        log.debug("auth: {}", auth);
        log.info("serial number: {}", serialNumber);
        
        // 这里可做后续处理,比如将session存入全局Map管理
        // sessionMap.put(serialNumber, session);
    }

    @Override
    public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception {
        log.info("收到消息:{}", message.getPayload());
        // 处理消息逻辑
    }

    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
        log.error("传输错误:{}", exception.getMessage());
    }

    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception {
        log.info("连接关闭:{}", closeStatus);
    }

    @Override
    public boolean supportsPartialMessages() {
        return false;
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 12:44:55