日常记录:springboot 给websocket 添加拦截、认证(一)

前言

嗯。这用jwt做认证校验时候,拦截器居然拦截不到@ServerEndpoint的地址,神奇了。

然后用了另外一种方式拦截。。

一、最初处理方式

之前是有这样一个类,读取日志信息

package com.sakyoka.test.webscoketlog.websocket;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;

import org.springframework.stereotype.Component;

import com.sakyoka.test.utils.Command;

import lombok.extern.log4j.Log4j;

/**
 * 
 * 描述:读取日志信息
 * @author sakyoka
 * @date 2022年8月14日 上午11:01:14
 */
@ServerEndpoint("/websocket/log")
@Log4j
@Component
public class WebSocketLog {

    private Process process;
    
    private InputStream inputStream;
    
    private String jarId;

    private static final ExecutorService EXECUTOR_SERVICE = Executors.newCachedThreadPool();
    
    public static final String SYSTEM_FLAG = "system";

    @OnOpen
    public void onOpen(Session session) {

    	Map<String, List<String>> params = session.getRequestParameterMap(); 
    	//暂时没有用处,但是如果用jarId关联每一个jar可以找到对应的日志文件,目前用system为例
    	jarId = params.get("jarId").get(0);
    	String logPath = null;
    	if (SYSTEM_FLAG.equals(jarId)) {
    		//logging.file=D:\\system.log
    		logPath = "D:\\system.log";
    	}else {
            if (params.containsKey("logPath")){
            	logPath = params.get("logPath").get(0);
            }
            //或 根据jarId 获取日志文件路径
    	}
        //window系统 tail命令需要添加tail.exe小工具到system32
        String cmd = "tail -f " +logPath; 
        
        log.debug(String.format("show log cmd >> %s", cmd));
		
		Command command = Command. getBuilder().commandStr(cmd).autoReadStream(false);
		command.exec();
		process = command.getProcess();
		inputStream = process.getInputStream();
		
		EXECUTOR_SERVICE.execute(() -> {
	        String line;
	        BufferedReader reader = null;
	        try {
	        	reader = new BufferedReader(new InputStreamReader(inputStream));
	            while((line = reader.readLine()) != null) {
	                session.getBasicRemote().sendText(line + "<br>");
	            }
	        } catch (IOException e) {
	        	
	        }
		});

    }

	@OnMessage
    public void onMessage(String message, Session session){
		log.debug(String.format("socket onmessage ==> 接收到信息:%s", message));
    }

    @OnClose
    public void onClose(Session session) {
    	this.close();
    	log.debug(String.format("socket已关闭"));
    }

    @OnError
    public void onError(Throwable thr) {
    	this.close();
    	log.debug(String.format("socket异常,errorMessage:%s" , thr.getMessage()));
    }
    
    private void close(){
    	
    	//这里应该先停止命令, 然后再关闭流
        if(process != null){
        	process.destroy();
        }

        try {
			if (Objects.nonNull(inputStream)){
				inputStream.close();
			}
		} catch (Exception e) {}   
    }
}

CommonInterceptor拦截器

package com.sakyoka.test.systemconfig.interceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import com.sakyoka.test.utils.JwtTokenUtils;

import lombok.extern.log4j.Log4j;

/**
 * 
 * 描述:请求拦截器
 * @author sakyoka
 * @date 2022年9月2日 下午1:54:41
 */
@Component
@Log4j
public class CommonInterceptor implements HandlerInterceptor{
	
	private static final int UNAUTHORIZED = 401;

	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		
		//从header中获取token值
		String token = request.getHeader("token");
		token = StringUtils.isBlank(token) ? request.getParameter("token") : token;
		if (StringUtils.isBlank(token)){
			log.debug("header token is null");
			response.setStatus(UNAUTHORIZED);
			response.sendRedirect("/login");
			return false;
		}
		
		//判断token有效性
		boolean tokenValid = JwtTokenUtils.tokenValid(token);
		if (!tokenValid){
			log.debug("invalid token");
			response.sendRedirect("/login");
			response.setStatus(UNAUTHORIZED);
			return false;
		}
		
		return true;
	}

	@Override
	public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
			ModelAndView modelAndView) throws Exception {

	}

	@Override
	public void afterCompletion(HttpServletRequest request, 
			HttpServletResponse response, Object handler, Exception ex)throws Exception {

	}

}

注册拦截器

package com.sakyoka.test.systemconfig.web;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import com.sakyoka.test.systemconfig.interceptor.CommonInterceptor;

/**
 * 
 * 描述:web配置
 * @author sakyoka
 * @date 2022年9月2日 下午1:59:04
 */
@Configuration
public class WebConfig extends WebMvcConfigurerAdapter{

	@Autowired
	CommonInterceptor commonInterceptor;
		
	@Override
	public void addInterceptors(InterceptorRegistry registry) {
		//拦截所有/log 开头接口
		registry.addInterceptor(commonInterceptor)
				.addPathPatterns("/log/**", "/websocket/log");
	}
}

发现不携带token,/websocket/log 还是能正常访问。

查阅资料还有另外一种处理,实现WebSocketConfigurer接口,在registerWebSocketHandlers方法里面添加处理器、拦截器、拦截地址

package com.sakyoka.test.systemconfig.socket;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.AbstractWebSocketMessageBrokerConfigurer;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
import org.springframework.web.socket.handler.TextWebSocketHandler;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

import com.sakyoka.test.systemconfig.interceptor.WebSocketReadLogInterceptor;

/**
 * 
 * 描述:开启WebSocket、注册ServerEndpointExporter实例、开启STOMP协议来传输基于代理
 * @author sakyoka
 * @date 2022年8月14日 2022
 */
@Configuration
@EnableWebSocket
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer implements WebSocketConfigurer{

	@Autowired
	WebSocketReadLogInterceptor webSocketReadLogInterceptor;
	
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
    
	@Override
	public void registerStompEndpoints(StompEndpointRegistry registry) {
		//添加一个stomp协议的endpoint
		registry.addEndpoint("/server").withSockJS();
	}
	
	@Override
	public void configureMessageBroker(MessageBrokerRegistry registry) {
		//添加一个topic代理节点
		registry.enableSimpleBroker("/topic");
	}
	
	@Override
	public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
		
		registry.addHandler(new TextWebSocketHandler(), "/websocket/log")
		        .setAllowedOrigins("*")
		        .addInterceptors(webSocketReadLogInterceptor);
	}
}

然而,在不携带token情况下还是能正常访问。

或者自定义一个WebSocketHandler,发现里面的方法没有输出信息。

但是在注释WebSocketLog的@Component之后,发现拦截成功了,而且自定义的WebSocketHandler对应方法也有信息了。。这里猜测是@ServerEndpoint覆盖了egistry.addHandler(new TextWebSocketHandler(), "/ws/handler/jarlog"),拦截地址,但是又没有对应的拦截器。

这里,我在想用自定义WebSocketHandler实现类算了,跟ServerEndpoint功能一毛一样。

二、新处理方式,另外一种websocket消息处理方式

放弃@ServerEndpoint,用自定义实现WebSocketHandler接口处理消息。

自定义TokenWebSocketHandler,在对应的连接、发送消息、异常、关闭连接对应把逻辑添加进去,由于TokenWebSocketHandler是单例(不像@ServerEndpoint,每次访问都是一个新实例),所以要全局变量存储每一个连接对应,关闭后释放。

package com.sakyoka.test.webscoketlog.websocket;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.WebSocketMessage;
import org.springframework.web.socket.WebSocketSession;

import com.sakyoka.test.utils.Command;
import com.sakyoka.test.webscoketlog.model.WebSocketBean;

import lombok.extern.log4j.Log4j;

/**
 * 
 * 描述:@see WebSocketLog 对应改造。使用WebSocketHandler处理消息
 * @author sakyoka
 * @date 2022年9月2日 下午4:15:24
 */
@Log4j
@Component
public class TokenWebSocketHandler implements WebSocketHandler{

	/**存储session id 对应的数据*/
	private static final Map<String, WebSocketBean> sessionLogSocketBeanMap = 
			new ConcurrentHashMap<String, WebSocketBean>(24);
    
	private static final ExecutorService EXECUTOR_SERVICE = Executors.newCachedThreadPool();
	
    public static final String SYSTEM_FLAG = "system";
    
	@Override
	public void afterConnectionEstablished(WebSocketSession session) throws Exception {
		
		WebSocketBean jarLogWebSocketBean = new WebSocketBean();
		jarLogWebSocketBean.setSessionId(session.getId());
		sessionLogSocketBeanMap.put(session.getId(), jarLogWebSocketBean);
		
		Map<String, Object> paramsMap = session.getAttributes();
    	String jarId = (String)paramsMap.get("jarId");
    	jarLogWebSocketBean.setJarId(jarId);
        log.debug(String.format("接收到jarId:%s, sessionid:%s", jarId, session.getId()));
        
        if (paramsMap.containsKey("showMessage")){
        	boolean showMessage = Boolean.valueOf(paramsMap.get("showMessage").toString());
        	jarLogWebSocketBean.setShowMessage(showMessage);
        }
		
    	String logPath = null;
    	if (SYSTEM_FLAG.equals(jarId)) {
    		//logging.file=D:\\system.log
    		logPath = "D:\\system.log";
    	}else {
            if (paramsMap.containsKey("logPath")){
            	logPath = (String)paramsMap.get("logPath");
            }
            //或 根据jarId 获取日志文件路径
    	}
        //window系统 tail命令需要添加tail.exe小工具到system32
        String cmd = "tail -f " +logPath; 
        
        log.debug(String.format("show log cmd >> %s", cmd));
        
        log.debug(String.format("show log cmd >> %s", cmd));
		
		Command command = Command. getBuilder().commandStr(cmd).autoReadStream(false);
		command.exec();
		Process process = command.getProcess();
		jarLogWebSocketBean.setProcess(process);
		InputStream inputStream = process.getInputStream();
		jarLogWebSocketBean.setInputStream(inputStream);
		
		EXECUTOR_SERVICE.execute(() -> {
	        String line;
	        BufferedReader reader = null;
	        try {
	        	reader = new BufferedReader(new InputStreamReader(inputStream));
	            while((line = reader.readLine()) != null) {
	                session.sendMessage(new TextMessage(line + "<br>"));
	            }
	        } catch (IOException e) {
	        	
	        }
		});
	}

	@Override
	public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception {
		if (sessionLogSocketBeanMap.containsKey(session.getId())) {
			WebSocketBean jarLogWebSocketBean = sessionLogSocketBeanMap.get(session.getId());
			if (jarLogWebSocketBean.getShowMessage()) {
				log.debug(String.format("socket onmessage ==> 接收到信息:%s", message.getPayload()));
			}
		}
	}

	@Override
	public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
		if (sessionLogSocketBeanMap.containsKey(session.getId())) {
			log.debug(String.format("socket异常,errorMessage:%s", exception.getMessage()));
		}
		
	}

	@Override
	public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception {
		WebSocketBean webSocketBean = sessionLogSocketBeanMap.get(session.getId());
		if (Objects.nonNull(webSocketBean.getProcess())) {
			webSocketBean.getProcess().destroy();
		}
		if (Objects.nonNull(webSocketBean.getInputStream())) {
			try {
				webSocketBean.getInputStream().close();
			} catch (Exception e) {}
		}

		webSocketBean = null;
		log.debug(String.format("socket已关闭,sessionid:%s, closeStatus:%s" , session.getId(), closeStatus));
		sessionLogSocketBeanMap.remove(session.getId());
	}

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

}

自定义WebSocketReadLogInterceptor拦截器,实现HandshakeInterceptor接口,只要处理握手前的方法即可,校验token

package com.sakyoka.test.systemconfig.interceptor;

import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang3.StringUtils;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.http.server.ServletServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.server.HandshakeInterceptor;

import com.sakyoka.test.systemconfig.result.ResultBean;
import com.sakyoka.test.utils.JwtTokenUtils;

import lombok.extern.log4j.Log4j;


/**
 * 
 * 描述:websocket 拦截
 * @author sakyoka
 * @date 2022年9月2日 下午3:51:53
 */
@Component
@Log4j
public class WebSocketReadLogInterceptor implements HandshakeInterceptor{

	@Override
	public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler,
			Map<String, Object> attributes) throws Exception {
		HttpServletRequest rs = ((ServletServerHttpRequest) request).getServletRequest();
		HttpServletResponse hp = ((ServletServerHttpResponse)response).getServletResponse();
		String token = rs.getParameter("token");
		if (StringUtils.isBlank(token)){
			log.info("token is null");
			hp.setStatus(Integer.valueOf(ResultBean.UNAUTHORIZED));
			return false;
		}
		
		//判断token有效性
		boolean tokenValid = JwtTokenUtils.tokenValid(token);
		if (!tokenValid){
			log.info("invalid token ");
			hp.setStatus(Integer.valueOf(ResultBean.UNAUTHORIZED));
			return false;
		}
		
		//request 参数放入 attributes中
		rs.getParameterMap().forEach((k, v) -> {
			attributes.put(k, v[0]);
		});
		
		return true;
	}

	@Override
	public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler,
			Exception exception) {

	}
}

添加处理器、添加拦截地址、添加拦截器

package com.sakyoka.test.systemconfig.socket;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.AbstractWebSocketMessageBrokerConfigurer;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

import com.sakyoka.test.systemconfig.interceptor.WebSocketReadLogInterceptor;
import com.sakyoka.test.webscoketlog.websocket.TokenWebSocketHandler;

/**
 * 
 * 描述:开启WebSocket、注册ServerEndpointExporter实例、开启STOMP协议来传输基于代理
 * @author sakyoka
 * @date 2022年8月14日 2022
 */
@Configuration
@EnableWebSocket
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer implements WebSocketConfigurer{

	@Autowired
	WebSocketReadLogInterceptor webSocketReadLogInterceptor;
	
	@Autowired
	TokenWebSocketHandler tokenWebSocketHandler;
	
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
    
	@Override
	public void registerStompEndpoints(StompEndpointRegistry registry) {
		//添加一个stomp协议的endpoint
		registry.addEndpoint("/server").withSockJS();
	}
	
	@Override
	public void configureMessageBroker(MessageBrokerRegistry registry) {
		//添加一个topic代理节点
		registry.enableSimpleBroker("/topic");
	}
	
	@Override
	public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
		
		// /ws/websocket/log 路径就是前端要访问的路径 类似@ServerEndpoint("/websocket/log")
		//添加处理器、添加拦截地址、添加拦截器
		registry.addHandler(tokenWebSocketHandler, "/ws/websocket/log")
		        .setAllowedOrigins("*")
		        .addInterceptors(webSocketReadLogInterceptor);
	}
}

页面处理

/websocket/log地址改成/ws/websocket/log,追加token参数

<%@ page contentType="text/html; charset=UTF-8" %>
<!DOCTYPE html>
<html>
<head> 
    <title>jarLog</title>
	<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
	<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
	<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
	<meta name="renderer" content="webkit">
    <jsp:include page="/WEB-INF/views/common/commonstatic.jsp" flush="true" />
    <!-- socketjs插件 -->
    <script type="text/javascript" src="${root}/components/socketjs/sockjs.js"></script>
    <script type="text/javascript" src="${root}/components/socketjs/stomp.js"></script>
    <!-- jquery菜单 -->
    <link rel="stylesheet" type="text/css" href="${root}/components/jquery/jquery.contextMenu.css"/>
    <script type="text/javascript" src="${root}/components/jquery/jquery.contextMenu.js"></script>
</head>
<body>
<div style="background-color:black;width:99%; height:500px; padding: 10px" id="console-parent">
    <div id="console" style="width:99%; height:95%; color:white;overflow-y: auto; overflow-x:hidden;"></div>
</div>
</body>
<script type="text/javascript" src="${root}/js/console.js"></script>
<script type="text/javascript" src="${root}/js/console-websocket.js"></script>
<script type="text/javascript" src="${root}/js/console-websocket-stomp.js"></script>
<script type="text/javascript">
//已在登录时候设置到缓存里面
var token = localStorage.getItem("token");
//var port = window.location.port;//如果经过代理?这个经过网关是网关的端口
//var port = "${pageContext.request.serverPort}";//这个才是后端端口
var jarWebSocket;
var jarConsole;
//jarId根据实际定义传过来,现在只有本系统的日志打印可以定义为system,其它jar的可以定义一个uuid关联标识
var jarId = "system";
//@ServerEndpoint("/log")
var wsurl = 'ws://'+ ip +':'+ port + root +'/websocket/log?jarId=' + jarId + "&token=" + token;
//registry.addEndpoint("/server").withSockJS();
var serverurl = 'http://'+ ip +':'+ port + root + '/server';
$(function(){
	//添加console-parent内容变化,调整滚动条位置,自动滚动最下面
	$("#console").bind("DOMNodeInserted",function(e){
	 	var height = $(this).prop("scrollHeight");
	 	$(this).animate({scrollTop: height}, 10);		
	});
	
	registerConsoleLogSocket();
	
	registerEmptyLogSocket();
	
	addRightClickListener();
});

/**
 * 注册控制台打印的socket事件
 */
function registerConsoleLogSocket(){
	jarConsole = new JarConsole();
	jarConsole.load('console');
    jarWebSocket= new JarWebSocket({
		url: wsurl,
		//获取后台返回信息
		onmessage: function(event){
			jarConsole.fill(event.data);
		}
	
	}).addEventListener();
}

/**
 * 注册清空日志的socket事件,采用消息订阅形式
 */
function registerEmptyLogSocket(){
	new StompSocketDefine({
		serverUrl: serverurl,
		subscribes: [{
			subscribeUrl: '/topic/emptylog',
			onmessage: function(res){
				//后端通知需要重新连接
				if (res.body == jarId && jarWebSocket.isConnect()){
					jarWebSocket.close();
					jarWebSocket.reset().reconnect();	
				}
			}
		}]
	}).connect();
}

/**
 * 添加右键菜单
 */
function addRightClickListener(){
	var items = {
	         "clear": {
	        	 name: "清空控制台信息", 
	        	 callback: function(){
	        		 jarConsole.clear();
	          }},
	         'emptyLog': {
	        	 name: "清空日志文件内容",
	        	 callback: function(){
	        		 var url = root + '/log/emptylog';
	        		 $.get(url, {jarId: jarId}, function(res){
	        			 console.log("emptylog result >>> " + res);
	        		 });
	        	 }
	         }
    };
	
	//右键菜单
    $.contextMenu({
        selector: '#console', 
        events:{preShow: function(){}},
        items: items
    });	
}
</script>
</html>	

三、测试

1、带token的

 socket读取日志信息正常现实。

2、不带token

 

可以看到,页面日志现实失败,后台打印信息,token is null

拦截ok!

总结

1、是不是发现其实WebSocketHandler就是@ServerEndpoint的另外一种集成websocket方式呢,所以拦截地址才没有生效

		registry.addHandler(tokenWebSocketHandler, "/ws/websocket/log")
		        .setAllowedOrigins("*")
		        .addInterceptors(webSocketReadLogInterceptor);

tokenWebSocketHandler对应的请求地址就是/ws/websocket/log

附件

springboot+jwt+websocket+拦截-Java文档类资源-CSDN下载

拓展

 有兴趣可以看看,最初springboot websocket集成实时读取日志、消息广播、登录认证

springboot集成websocket 实时输出日志到浏览器(一)_sakyoka的博客-CSDN博客_websocket实时显示日志

springboot集成websocket 清空日志后消息广播通知前端重新连接(二)_sakyoka的博客-CSDN博客

springboot 集成JWT 登录验证_sakyoka的博客-CSDN博客

都是在一个项目上慢慢添加的功能。

  • 14
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 11
    评论
以下是在Spring Boot项目中整合WebSocket的步骤,包括拦截器、消息处理器和WebSocket实现类的编写: 1. 添加依赖 在pom.xml文件中添加以下依赖: ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency> ``` 2. 编写WebSocket配置类 创建一个WebSocketConfig类,用于配置WebSocket相关的内容,包括注册WebSocket处理器、拦截器等。 ```java @Configuration @EnableWebSocket public class WebSocketConfig implements WebSocketConfigurer { @Autowired private MyWebSocketHandler myWebSocketHandler; @Autowired private MyHandshakeInterceptor myHandshakeInterceptor; @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { registry.addHandler(myWebSocketHandler, "/websocket") .addInterceptors(myHandshakeInterceptor); } } ``` 在这个配置类中,我们注册了一个WebSocket处理器和一个握手拦截器。WebSocket处理器用于处理WebSocket连接和消息,握手拦截器用于拦截WebSocket连接请求并进行一些处理。 3. 编写WebSocket处理器 创建一个WebSocket处理器类,用于处理WebSocket连接和消息。 ```java @Component public class MyWebSocketHandler extends TextWebSocketHandler { private static final Logger logger = LoggerFactory.getLogger(MyWebSocketHandler.class); private static final Map<String, WebSocketSession> sessions = new ConcurrentHashMap<>(); @Override public void afterConnectionEstablished(WebSocketSession session) throws Exception { logger.info("WebSocket连接建立成功:{}", session.getId()); sessions.put(session.getId(), session); } @Override protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception { logger.info("收到消息:{}", message.getPayload()); // 处理消息 } @Override public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception { logger.info("WebSocket连接关闭:{},状态:{}", session.getId(), status); sessions.remove(session.getId()); } public static void sendMessage(String sessionId, String message) throws IOException { WebSocketSession session = sessions.get(sessionId); if (session != null && session.isOpen()) { session.sendMessage(new TextMessage(message)); } } } ``` 在这个处理器类中,我们重写了WebSocket处理器的三个方法:afterConnectionEstablished、handleTextMessage和afterConnectionClosed。afterConnectionEstablished方法在WebSocket连接建立成功后被调用,handleTextMessage方法用于处理收到的消息,afterConnectionClosed方法在WebSocket连接关闭后被调用。 4. 编写握手拦截器 创建一个握手拦截器类,用于拦截WebSocket连接请求并进行一些处理。 ```java @Component public class MyHandshakeInterceptor implements HandshakeInterceptor { private static final Logger logger = LoggerFactory.getLogger(MyHandshakeInterceptor.class); @Override public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception { logger.info("WebSocket握手拦截器:beforeHandshake"); // 进行一些处理 return true; } @Override public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception exception) { logger.info("WebSocket握手拦截器:afterHandshake"); } } ``` 在这个握手拦截器类中,我们重写了握手拦截器的两个方法:beforeHandshake和afterHandshake。beforeHandshake方法在WebSocket连接请求到达服务器端时被调用,afterHandshake方法在WebSocket连接握手成功后被调用。 5. 编写WebSocket实现类 创建一个WebSocket实现类,用于实现WebSocket的具体业务逻辑。 ```java @Controller public class MyWebSocketController { @MessageMapping("/hello") @SendTo("/topic/greetings") public String greeting(String message) throws Exception { Thread.sleep(1000); // 模拟处理时间 return "Hello, " + message + "!"; } } ``` 在这个WebSocket实现类中,我们使用了@MessageMapping注解来指定处理消息的路径,使用@SendTo注解来指定返回消息的路径。 6. 测试WebSocket连接 在客户端中,可以使用JavaScript代码来测试WebSocket连接: ```javascript var socket = new WebSocket("ws://localhost:8080/websocket"); socket.onopen = function(event) { console.log("WebSocket连接已建立"); socket.send("Hello, WebSocket!"); }; socket.onmessage = function(event) { console.log("收到消息:" + event.data); }; socket.onclose = function(event) { console.log("WebSocket连接已关闭"); }; ``` 以上就是在Spring Boot项目中整合WebSocket的步骤,包括拦截器、消息处理器和WebSocket实现类的编写。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值