學(xué)習(xí)地址:https://blog.csdn.net/qq_43898141/article/details/123744468
- 添加websocket 依賴
<!--添加websockert依賴-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId><exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</exclusion>
</exclusions>
- 添加配置
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();
}
}
- webSocket 代碼
import com.fasterxml.jackson.annotation.JsonBackReference;
import com.ruoyi.common.core.config.systemConfig;
import com.ruoyi.common.core.utils.SpringUtils;
import com.ruoyi.common.redis.service.RedisService;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.io.Serializable;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@ServerEndpoint(value = "/websocket/{userName}")
@Component
public class WebSocketService implements Serializable {
//concurrent包的線程安全Set,用來存放每個客戶端對應(yīng)的WebSocketServer對象。
@JsonBackReference
private static Map<String, WebSocketService> webSocketMap = new ConcurrentHashMap<>();
/**
* 與某個客戶端的連接會話,需要通過它來給客戶端發(fā)送數(shù)據(jù)
*/
private Session session;
/**
* 接收userName
*/
private String userName = "";
/**
* 連接建立成功調(diào)用的方法
*/
@OnOpen
public void onOpen(Session session, @PathParam("userName") String userName) {
this.session = session;
this.userName = userName;
webSocketMap.put(userName, this);
System.err.println("----------------------------------------------------------------------------");
System.err.println("用戶連接:" + userName + ":" + userName + ",當(dāng)前在線人數(shù)為:" + webSocketMap.size());
sendMessage("來自后臺的反饋:連接成功");
webSocketMap.forEach((k, v) -> System.err.println(k));
}
/**
* 連接關(guān)閉調(diào)用的方法
*/
@OnClose
public void onClose(@PathParam("userName") String userName) {
if (webSocketMap.containsKey(userName)) {
webSocketMap.remove(userName);
}
System.err.println("----------------------------------------------------------------------------");
System.err.println(userName + "用戶退出,當(dāng)前在線人數(shù)為:" + webSocketMap.size());
}
/**
* 收到客戶端消息后調(diào)用的方法
*
* @param message 客戶端發(fā)送過來的消息
*/
@OnMessage
public void onMessage(String message, Session session) {
System.err.println("收到用戶消息:" + userName + ",報(bào)文:" + message);
//可以群發(fā)消息
//消息保存到數(shù)據(jù)庫、redis
if (message != null) System.err.println("");
}
/**
* 連接失敗調(diào)用方法
* @param session
* @param error
*/
@OnError
public void onError(Session session, Throwable error) {
System.err.println("用戶錯誤:" + this.userName + ",原因:" + error.getMessage());
error.printStackTrace();
}
/**
* 連接服務(wù)器成功后主動推送
*/
public void sendMessage(String message) {
try {
session.getBasicRemote().sendText(message);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 向指定客戶端發(fā)送消息
* <p>
* // * @param userName
* //* @param message
*/
public static void sendMessage(String userName, String message) {
try {
WebSocketService webSocketService = webSocketMap.get(userName);
if (webSocketService != null) {
webSocketService.getSession().getBasicRemote().sendText(message);
}
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException(e.getMessage());
}
}
//下面方法根據(jù)自己情況 刪 留
public Session getSession() {
return session;
}
public void setSession(Session session) {
this.session = session;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public static Map<String, WebSocketService> getWebSocketMap() {
return webSocketMap;
}
public static void setWebSocketMap(Map<String, WebSocketService> webSocketMap) {
WebSocketService.webSocketMap = webSocketMap;
}
public static void put(String key, WebSocketService data) {
webSocketMap.put(key, data);
}
public static WebSocketService get(String key) {
return webSocketMap.get(key);
}
public static void del(String key) {
webSocketMap.remove(key);
}
- 網(wǎng)管配置服務(wù)轉(zhuǎn)發(fā)
# websocket模塊
#服務(wù)名稱
- id: ruoyi-chat-websocket
#轉(zhuǎn)發(fā)的服務(wù)
uri: lb:ws://ruoyi-chat
#轉(zhuǎn)發(fā)設(shè)置
predicates:
- Path=/websocket/**
#請求地址后一位,如:/socket/xxx/xx 去掉socket = /xxx/xx
filters:
- StripPrefix=1
- 重要: 如果你的webSocket 沒有Token 也沒有任何認(rèn)證的話 需要開放白名單。
# 不校驗(yàn)白名單
ignore:
whites:
- /code
- /auth/logout
- /auth/login
- /auth/smsLogin
- /auth/xcxLogin
- /auth/mobileLogin
- /websocket/**
- **開始測試 **http://www.jsons.cn/websocket/
-
如果出現(xiàn)連接成功 立馬斷開的問題 排查思路如下
- 排查網(wǎng)關(guān)里面的轉(zhuǎn)發(fā)url 是否使用的ws=> lb:ws://ruoyi-chat
- 是否開啟了白名單
如果以上都可以了 還是不行,那就是因?yàn)樗淖幽K默認(rèn)也是需要Token 權(quán)限的 我在解決了兩天的情況下發(fā)現(xiàn) 需要加上以下配置
- 在 SecurityConfiguration implements WebMvcConfigurer 文件下面增加 放過
/**
* 校驗(yàn)是否從網(wǎng)關(guān)轉(zhuǎn)發(fā)
*/
@Bean
public SaServletFilter getSaServletFilter() {
return new SaServletFilter()
.addInclude("/**")
.addExclude("/actuator/**")
.addExclude("/socket/**")
.setAuth(obj -> SaIdUtil.checkCurrentRequestToken())
.setError(e -> SaResult.error("認(rèn)證失敗,無法訪問系統(tǒng)資源").setCode(HttpStatus.UNAUTHORIZED));
}
出現(xiàn)的錯誤提示文章來源:http://www.zghlxwxcb.cn/news/detail-425282.html
WebSocketClientHandshakeException Create breakpoint : Invalid handshake response getStatus: 200 OK
An exception has been observed post termination, use DEBUG level to see the full stack:io.netty . handler . codec . http . websocketx . webSocketClientHandshakeExcept ion: Connection prematurely closed BEFORE opening handshake is complete.
結(jié)論:
在子模塊中也是需要鑒權(quán)的Token 的如果只在網(wǎng)管放開白名單 在子模塊也是不行的 所以直接在這個地方加上文章來源地址http://www.zghlxwxcb.cn/news/detail-425282.html
import cn.dev33.satoken.filter.SaServletFilter;
import cn.dev33.satoken.id.SaIdUtil;
import cn.dev33.satoken.interceptor.SaAnnotationInterceptor;
import cn.dev33.satoken.util.SaResult;
import com.yawei.common.core.constant.HttpStatus;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* 權(quán)限安全配置
*
* @author Suiquantong
*/
@AutoConfiguration
public class SecurityConfiguration implements WebMvcConfigurer {
/**
* 注冊sa-token的攔截器
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 注解攔截器
registry.addInterceptor(new SaAnnotationInterceptor()).addPathPatterns("/**");
}
/**
* 校驗(yàn)是否從網(wǎng)關(guān)轉(zhuǎn)發(fā)
*/
@Bean
public SaServletFilter getSaServletFilter() {
return new SaServletFilter()
.addInclude("/**")
.addExclude("/actuator/**")
.addExclude("/socket/**")
.setAuth(obj -> SaIdUtil.checkCurrentRequestToken())
.setError(e -> SaResult.error("認(rèn)證失敗,無法訪問系統(tǒng)資源").setCode(HttpStatus.UNAUTHORIZED));
}
}
到了這里,關(guān)于RuoYi-Cloud-Plus集成 WebSocket的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!