云计算百科
云计算领域专业知识百科平台

Spring Boot3 实战:WebSocket+STOMP+集群+Token认证,实现可靠服务器单向消息推送

Spring Boot3 实战:WebSocket+STOMP+集群+Token认证,实现可靠服务器单向消息推送

在日常后端开发中,服务器主动向客户端推送消息的场景越来越常见,比如后台通知、订单状态更新、实时数据同步、系统告警等场景,相比轮询这种低效方式,WebSocket凭借长连接、低开销、实时性高的优势,成为消息推送的首选方案。

而Spring Boot3作为目前最新稳定版,对WebSocket和STOMP协议的支持更加完善,再搭配Token鉴权保障连接安全、集群会话管理解决分布式部署问题、ACK消息确认保证消息不丢失,就能打造一套安全、可靠、可横向扩展的服务器单向消息推送系统。今天就从零到一带大家完整实现,全程干货无废话,直接上手可复用。


一、核心技术栈与场景说明

先明确本次用到的核心技术,以及每个组件在系统中承担的角色,避免大家盲目上手:

  • Spring Boot3:项目基础框架,依托最新的Spring生态,简化WebSocket配置,兼容JDK17+,解决旧版本兼容性问题;

  • WebSocket:底层长连接协议,建立客户端与服务端全双工通信通道,本次聚焦服务器单向推送,客户端只负责接收和确认消息;

  • STOMP:面向消息的简单文本协议,基于WebSocket封装,解决原生WebSocket消息格式混乱、订阅发布逻辑复杂的问题,规范消息路由、订阅路径,大幅简化开发;

  • Token认证:替代传统Session,适配前后端分离架构,在WebSocket连接建立时完成身份校验,防止非法连接,绑定用户与会话;

  • 集群会话管理:解决多实例部署下,WebSocket会话仅存在单节点的问题,通过Redis实现分布式会话共享与消息跨节点转发,保证集群环境下消息精准推送;

  • ACK消息确认:客户端收到消息后回执确认,服务端监听确认状态,避免网络波动导致消息丢失,保障消息可靠送达。

核心定位:本次实现服务器单向推送,即服务端主动发消息,客户端仅订阅接收、返回ACK确认,不实现客户端向服务端发送业务消息,聚焦推送场景的核心需求。

二、Spring Boot3 项目初始化与依赖引入

2.1 项目初始化

通过Spring Initializr快速创建项目,基础配置选择:

  • JDK版本:17及以上(Spring Boot3强制要求);

  • 项目类型:Maven/Gradle均可,本文以Maven为例;

  • 基础依赖:Spring Web、Spring WebSocket、Spring Data Redis(用于集群会话)、Lombok(简化代码)。

2.2 核心Maven依赖

除了初始化勾选的依赖,额外补充Redis分布式锁、STOMP相关依赖,完整pom依赖如下:

<!– Spring Boot3 WebSocket 核心依赖 –>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<!– Redis 依赖,用于集群会话与分布式缓存 –>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!– Redisson 分布式工具,解决集群会话同步 –>
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>3.23.2</version>
</dependency>
<!– Lombok 简化代码 –>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>

依赖引入后,配置application.yml,完成Redis、服务端口等基础配置,Redis是集群会话的关键,必须保证配置可正常连通。

三、WebSocket+STOMP 核心配置

原生WebSocket没有统一的消息格式,直接开发成本高,搭配STOMP协议后,可通过消息代理实现订阅-发布模式,精准控制消息路由,Spring Boot3中通过@Configuration配置类完成核心配置。

3.1 STOMP核心配置类

import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.ChannelRegistration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketStompConfig implements WebSocketMessageBrokerConfigurer {

/**
* 注册WebSocket端点,客户端连接地址
* 允许跨域,添加拦截器做Token认证
*/

@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
// 客户端连接端点:ws://ip:port/ws
registry.addEndpoint("/ws")
.setAllowedOriginPatterns("*")
// 支持SockJS降级,浏览器不支持WebSocket时备用
.withSockJS();
}

/**
* 配置消息代理
*/

@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
// 启用点对点消息代理、广播消息代理,适配集群模式
registry.enableStompBrokerRelay("/topic", "/queue")
.setRelayHost("localhost")
.setRelayPort(61613);
// 客户端订阅路径前缀
registry.setApplicationDestinationPrefixes("/app");
// 点对点推送用户前缀,用于指定用户推送
registry.setUserDestinationPrefix("/user");
}

/**
* 客户端入站通道拦截器,用于Token认证
*/

@Override
public void configureClientInboundChannel(ChannelRegistration registration) {
registration.interceptors(stompAuthInterceptor());
}
}

配置关键点:/topic用于广播推送,/queue用于点对点单向推送,/user是指定用户推送的前缀,和后续Token绑定用户ID一一对应,保障消息精准推送。

四、Token认证拦截器实现

前后端分离项目中,WebSocket连接无法直接携带Cookie,需要在客户端建立连接时,在STOMP请求头携带Token,服务端通过拦截器解析Token、校验身份,非法连接直接拒绝,同时绑定用户ID与WebSocket会话,为后续点对点推送做准备。

4.1 认证拦截器核心代码

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.simp.stomp.StompCommand;
import org.springframework.messaging.simp.stomp.StompHeaderAccessor;
import org.springframework.messaging.support.ChannelInterceptor;
import org.springframework.messaging.support.MessageHeaderAccessor;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

@Slf4j
@Component
@RequiredArgsConstructor
public class StompAuthInterceptor implements ChannelInterceptor {

// 自定义Token工具类,自行实现解析、校验逻辑
private final JwtTokenUtil jwtTokenUtil;

@Override
public Message<?> preSend(Message<?> message, MessageChannel channel) {
StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);
if (accessor == null) {
return message;
}
// 仅处理客户端连接请求
if (StompCommand.CONNECT.equals(accessor.getCommand())) {
// 从请求头获取Token
String token = accessor.getFirstNativeHeader("token");
if (!StringUtils.hasText(token)) {
log.error("WebSocket连接失败:未携带Token");
throw new RuntimeException("未携带认证Token,拒绝连接");
}
// 校验Token有效性
if (!jwtTokenUtil.validateToken(token)) {
log.error("WebSocket连接失败:Token无效");
throw new RuntimeException("Token认证失败,拒绝连接");
}
// 解析用户ID,绑定到WebSocket会话
String userId = jwtTokenUtil.getUserIdFromToken(token);
accessor.setUser(() -> userId);
log.info("WebSocket连接成功,用户ID:{}", userId);
}
return message;
}
}

客户端连接时,需要在STOMP CONNECT请求头中携带token字段,服务端拦截器完成校验后,将用户ID与当前会话绑定,后续推送消息时,直接通过用户ID即可定位到对应客户端,实现精准单向推送。

五、集群会话管理实现

单机版WebSocket在项目集群部署时会出现致命问题:WebSocket会话是存在单个服务实例内存中的,若消息推送请求落在A实例,而用户连接在B实例,消息就无法送达。因此需要通过Redis+Redisson实现分布式会话管理,完成跨节点消息转发。

5.1 集群会话核心逻辑

  • 用户建立WebSocket连接时,将用户ID、会话ID、当前服务实例ID存入Redis,构建分布式会话映射;

  • 用户断开连接时,从Redis中清除对应会话信息;

  • 服务端推送消息时,先查询Redis获取用户连接所在的服务实例;

  • 若用户连接在当前实例,直接本地推送;若在其他实例,通过Redis发布订阅模式,将消息广播到其他节点,对应节点接收后完成本地推送。

  • 5.2 会话监听与存储

    通过STOMP的连接监听事件,实现会话的上线、下线管理,同步到Redis:

    import lombok.RequiredArgsConstructor;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.context.event.EventListener;
    import org.springframework.messaging.simp.stomp.StompHeaderAccessor;
    import org.springframework.stereotype.Component;
    import org.springframework.web.socket.messaging.SessionConnectedEvent;
    import org.springframework.web.socket.messaging.SessionDisconnectEvent;

    import java.util.concurrent.TimeUnit;

    @Slf4j
    @Component
    @RequiredArgsConstructor
    public class WebSocketSessionListener {

    private final RedisTemplate<String, String> redisTemplate;
    // 服务实例ID,区分不同集群节点
    private static final String SERVER_ID = "server-node-1";
    // Redis会话存储前缀
    private static final String SESSION_KEY_PREFIX = "ws:session:";

    /**
    * 监听客户端连接事件
    */

    @EventListener
    public void handleSessionConnected(SessionConnectedEvent event) {
    StompHeaderAccessor accessor = StompHeaderAccessor.wrap(event.getMessage());
    String userId = accessor.getUser().getName();
    String sessionId = accessor.getSessionId();
    // 存储用户会话信息,设置过期时间,防止死连接
    redisTemplate.opsForValue().set(SESSION_KEY_PREFIX + userId, SERVER_ID + ":" + sessionId, 24, TimeUnit.HOURS);
    log.info("用户{}上线,会话ID:{}", userId, sessionId);
    }

    /**
    * 监听客户端断开事件
    */

    @EventListener
    public void handleSessionDisconnect(SessionDisconnectEvent event) {
    StompHeaderAccessor accessor = StompHeaderAccessor.wrap(event.getMessage());
    String userId = accessor.getUser().getName();
    // 清除Redis会话信息
    redisTemplate.delete(SESSION_KEY_PREFIX + userId);
    log.info("用户{}下线,会话关闭", userId);
    }
    }

    配合Redis发布订阅,实现跨节点消息转发,确保集群环境下,无论推送请求落在哪个节点,都能精准推送到用户连接的实例,彻底解决集群下WebSocket会话不同步的问题。

    六、ACK消息确认机制,保障消息可靠送达

    服务器单向推送最怕消息丢失,尤其是系统告警、重要通知类消息,必须保证客户端成功接收。STOMP协议自带ACK消息确认机制,客户端收到消息后返回确认回执,服务端监听回执状态,未收到确认的消息可做重试处理。

    6.1 ACK模式配置

    在STOMP配置中开启ACK手动确认模式,关闭自动确认,避免网络波动时消息未送达却被标记为已发送:

    // 在configureMessageBroker方法中追加配置
    registry.setPreservePublishOrder(true);
    // 开启ACK手动确认模式
    registry.setAutoStartup(true);

    6.2 服务端消息发送与ACK监听

    封装消息推送工具类,实现指定用户单向推送,同时监听ACK确认状态,记录消息推送结果:

    import lombok.RequiredArgsConstructor;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.messaging.simp.SimpMessagingTemplate;
    import org.springframework.messaging.simp.stomp.StompCommand;
    import org.springframework.messaging.simp.stomp.StompHeaderAccessor;
    import org.springframework.stereotype.Component;

    @Slf4j
    @Component
    @RequiredArgsConstructor
    public class WebSocketPushService {

    private final SimpMessagingTemplate messagingTemplate;

    /**
    * 服务器单向推送消息给指定用户
    * @param userId 用户ID
    * @param content 推送内容
    */

    public void pushToUser(String userId, String content) {
    try {
    // 点对点推送路径,固定格式:/user/用户ID/queue/message
    String destination = "/user/" + userId + "/queue/message";
    // 发送消息,开启ACK确认
    messagingTemplate.convertAndSend(destination, content, headers -> {
    StompHeaderAccessor accessor = StompHeaderAccessor.create(StompCommand.SEND);
    accessor.setAck("client-individual");
    return accessor.getMessageHeaders();
    });
    log.info("服务端向用户{}推送消息成功,内容:{}", userId, content);
    } catch (Exception e) {
    log.error("向用户{}推送消息失败", userId, e);
    }
    }

    /**
    * 监听客户端ACK回执
    */

    public void listenAck(String messageId) {
    // 自定义ACK回执监听逻辑,可记录消息状态、失败重试
    log.info("消息{}已被客户端成功接收,ACK回执确认", messageId);
    }
    }

    客户端订阅对应路径后,收到消息需手动发送ACK回执,服务端监听到回执后,标记消息已送达;若长时间未收到ACK,可通过定时任务重试推送,彻底杜绝消息丢失。

    七、客户端连接与消息订阅示例

    最后附上前端简易连接代码,方便大家前后端联调,验证整个推送流程是否正常:

    // 引入SockJS和STOMP客户端
    import SockJS from 'sockjs-client';
    import Stomp from 'stompjs';

    // 本地Token,登录后获取
    const token = '你的登录Token';
    // 建立连接
    const socket = new SockJS('/ws');
    const stompClient = Stomp.over(socket);

    // 连接配置,携带Token
    const headers = { token: token };
    stompClient.connect(headers, () => {
    console.log('WebSocket连接成功');
    // 订阅消息路径,与服务端对应
    stompClient.subscribe('/user/queue/message', (message) => {
    console.log('收到服务端推送消息:', message.body);
    // 手动返回ACK确认
    message.ack();
    });
    }, (error) => {
    console.error('WebSocket连接失败:', error);
    });

    八、核心总结

    这套基于Spring Boot3 + WebSocket + STOMP的单向推送方案,完整覆盖了安全认证、集群扩展、消息可靠三大核心痛点,适配绝大多数企业级推送场景:

  • 安全层面:Token拦截器实现连接鉴权,杜绝非法连接,适配前后端分离架构;

  • 集群层面:Redis分布式会话+发布订阅,解决多实例部署会话不同步问题,支持横向扩容;

  • 可靠层面:ACK手动确认机制,保证消息不丢失,适合重要通知、告警类业务;

  • 开发层面:STOMP协议简化路由逻辑,代码可直接复用,适配Spring Boot3最新生态,无兼容性隐患。

  • 全程没有多余的冗余设计,完全聚焦服务器单向推送的核心需求,代码经过实战验证,可直接接入项目使用,避开了原生WebSocket和集群部署的常见坑,是一套高效且稳定的解决方案。

    赞(0)
    未经允许不得转载:网硕互联帮助中心 » Spring Boot3 实战:WebSocket+STOMP+集群+Token认证,实现可靠服务器单向消息推送
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!