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关闭。

相关推荐
weixin_440784116 小时前
【HandlerThread实现原理】
android·java·开发语言
leoZ2316 小时前
本地跑大模型实战(七):llama.cpp 性能调优,让推理更快更省
java·人工智能·spring·生成对抗网络·语言模型·自然语言处理·llama
有梦不弃6 小时前
模块化单体架构设计方案:DDD + 六边形架构落地实践
后端·架构
mubei-1237 小时前
SpringDAO的用法
java·开发语言·数据库
happymagic7 小时前
java spring boot做的jar包程序,如何实现自动运行启动
java·运维·服务器·spring boot·jar
李昊哲小课9 小时前
fastapi sse websocket 奶茶店实时订单看板
人工智能·python·websocket·网络协议·fastapi·sse
都叫我大帅哥10 小时前
给 Java 程序员的 CSRF 攻击详解:从原理到防御实战
java
江南十四行10 小时前
Spring框架核心(上)——IoC控制反转与DI依赖注入详解
java·后端·spring
IT_陈寒11 小时前
Redis缓存击穿把我坑惨了,原来这样设过期时间才靠谱
前端·人工智能·后端
fb_1234511 小时前
Linux磁盘分区从入门到实操:MBR_GPT全解析+分区工具实战指南
java·linux·gpt