Springboot整合Websocket实现ws和wss连接

1. 引入pom依赖

xml 复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-websocket</artifactId>
    <version>2.7.10</version>
</dependency>

2. 新建websocket配置文件

java 复制代码
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

@Configuration
public class WebSocketConfig {

    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
}

3. 新建websocket常量配置

java 复制代码
public class WebsocketConst {

    /**
     * 消息类型 heartcheck
     */
    public static final String CMD_CHECK = "heartcheck";
}

4. 编写websocket代码

java 复制代码
import jakarta.websocket.*;
import jakarta.websocket.server.PathParam;
import jakarta.websocket.server.ServerEndpoint;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@Component
@Slf4j
@ServerEndpoint("/websocket/{userId}")
public class WebSocketServer {

    /**线程安全Map*/
    private static ConcurrentHashMap<String, Session> sessionPool = new ConcurrentHashMap<>();

    //==========【websocket接受、推送消息等方法 ------ 具体服务节点推送ws消息】=====================================================
    @OnOpen
    public void onOpen(Session session, @PathParam(value = "userId") String userId) {
        try {
            sessionPool.put(userId, session);
            log.info("【系统 WebSocket】有新的连接,总数为:" + sessionPool.size());
        } catch (Exception e) {
        }
    }

    @OnClose
    public void onClose(@PathParam("userId") String userId) {
        try {
            sessionPool.remove(userId);
            log.info("【系统 WebSocket】连接断开,总数为:" + sessionPool.size());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * ws推送消息
     *
     * @param userId
     * @param message
     */
    public void pushMessage(String userId, String message) {
        for (Map.Entry<String, Session> item : sessionPool.entrySet()) {
            //userId key值= {用户id + "_"+ 登录token的md5串}
            if (item.getKey().contains(userId)) {
                Session session = item.getValue();
                try {
                    synchronized (session){
                        log.info("【系统 WebSocket】推送单人消息:" + message);
                        session.getBasicRemote().sendText(message);
                    }
                } catch (Exception e) {
                    log.error(e.getMessage(),e);
                }
            }
        }
    }

    /**
     * ws遍历群发消息
     */
    public void pushMessage(String message) {
        try {
            for (Map.Entry<String, Session> item : sessionPool.entrySet()) {
                try {
                    item.getValue().getAsyncRemote().sendText(message);
                } catch (Exception e) {
                    log.error(e.getMessage(), e);
                }
            }
            log.info("【系统 WebSocket】广播消息:" + message);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }

    /**
     * ws接受客户端消息
     */
    @OnMessage
    public void onMessage(String message, @PathParam(value = "userId") String userId) {
        if(!"ping".equals(message) && !WebsocketConst.CMD_CHECK.equals(message)){
            log.info("【系统 WebSocket】收到客户端消息:" + message);
        }else{
            log.debug("【系统 WebSocket】收到客户端消息:" + message);
        }
    }

    /**
     * 配置错误信息处理
     *
     * @param session
     * @param t
     */
    @OnError
    public void onError(Session session, Throwable t) {
        log.warn("【系统 WebSocket】消息出现错误");
        t.printStackTrace();
    }
    //==========【系统 WebSocket接受、推送消息等方法 ------ 具体服务节点推送ws消息】=================================================


}

5. ws连接测试

6. wss连接配置

  1. 生成ssl证书

    shell 复制代码
    openssl req -newkey rsa:2048 -nodes -keyout server.key -x509 -days 3650 -out server.crt
  2. nginx配置

    conf 复制代码
    server {
            listen    8443 ssl;
            server_name  localhost;
            ssl_certificate      /etc/nginx/conf.d/certs/server.crt;
            ssl_certificate_key  /etc/nginx/conf.d/certs/server.key;
            ssl_session_cache    shared:SSL:1m;
            ssl_session_timeout  5m;
            ssl_ciphers  HIGH:!aNULL:!MD5;
            ssl_prefer_server_ciphers  on;
            root html;
                  charset 'utf-8';
    
            location / {
                root   /dsp/app/dsp-web/dist/; #你项目在系统中的路径
                try_files $uri $uri/ /index.html;
                index  index.html index.htm;
            }
    
    		# 拦截API
            location ^~ /prod-api {
              proxy_pass http://dsp-gateway:9999/;
              proxy_redirect off;
              rewrite /prod-api/(.*)$ /$1 break;
              proxy_set_header Host $http_host;
              proxy_set_header X-Real-IP $remote_addr;
              proxy_set_header REMOTE-HOST $remote_addr;
              proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            }
    
    		# 拦截websocket
            location /websocket/ {
              proxy_pass http://dsp-gateway:9999/;
              proxy_redirect off;
              proxy_http_version 1.1;
              proxy_set_header Upgrade $http_upgrade;
              proxy_set_header Connection "upgrade";
              proxy_set_header Host $host:$server_port;
              proxy_set_header X-Real-IP $remote_addr;
              proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
           }
    
    }
  3. wss连接测试

注意:使用postman测试wss时,可能会报证书验证失败的问题,如下:

可能是SSL认证拦截了请求,可以在postman的 设置 中将 SSL certificate verification关闭。

相关推荐
程序员小郭831 分钟前
Spring Ai 04 解决 ChatClient 初始化冲突问题
java·后端·spring
y = xⁿ3 分钟前
【LeetCodehot100】T114:二叉树展开为链表 T105:从前序与中序遍历构造二叉树
java·算法·链表
SuniaWang4 分钟前
《Spring AI + 大模型全栈实战》学习手册系列 · 专题八:《RAG 系统安全与权限管理:企业级数据保护方案》
java·前端·人工智能·spring boot·后端·spring·架构
xiaohe0731 分钟前
Maven Spring框架依赖包
java·spring·maven
不吃香菜学java1 小时前
苍穹外卖-菜品分页查询
数据库·spring boot·tomcat·log4j·maven·mybatis
hssfscv1 小时前
软件设计师下午题二 E-R图
java·笔记·学习
2301_805962931 小时前
ESP32远程OTA升级:从局域网到公网部署
网络·后端·http·esp32
cyforkk1 小时前
Spring Boot 3 集成 Swagger 踩坑实录:解决 doc.html 404 与 Unauthorized 拦截
spring boot·后端·html
十七号程序猿1 小时前
Java图书管理系统 | 无需配置任何环境,双击一键启动,开箱即用
java·spring boot·vue·毕业设计·毕设·源代码管理
宝耶1 小时前
Java面试2:final、finally、finalize 的区别?
java·开发语言·面试