在之前搭建好的项目的基础上新版security demo(二)前端-CSDN博客
目录
一、代码改造
1、后端改造
2、VUE使用websocket
3、测试
二、按用户推送
1、完整代码如下
1.1、前端
1.2、后端:
2、测试
一、代码改造
1、后端改造
(1)把websocket相关代码复制到web项目:
(2)添加白名单
web-socket端口无需token验证,SecurityWebConfig添加白名单
@Beanpublic WebSecurityCustomizer webSecurityCustomizer() {return (web) -> web.ignoring().requestMatchers("/param/**", "/user-websocket-endpoint/**","/menu-websocket-endpoint/**");}
2、VUE使用websocket
<template><div>this is user manage</div>
</template>
<script>
export default {data() {return {socket: null,message: '',inputMessage: ''};},mounted() {// Create a new WebSocket connectionthis.socket = new WebSocket("ws://localhost:2222/securityDemo/user-websocket-endpoint");// Set up event listenersthis.socket.onopen = (event) => {console.log('WebSocket connection opened.');this.socket.send("Hello Server! This is menu client");};this.socket.onmessage = (event) => {this.message = event.data;};this.socket.onerror = (error) => {console.error('WebSocket Error:', error);};this.socket.onclose = (event) => {console.log('WebSocket connection closed.');};},methods: {sendMessage() {if (this.socket && this.inputMessage) {this.socket.send(this.inputMessage);this.inputMessage = ''; // Clear the input field after sending}}},// beforeDestroy() {// // Close the WebSocket connection when the component is destroyed// if (this.socket) {// this.socket.close();// }// }
};
</script>
menumanage.vue代码和这个类似。
3、测试
分别启动前后端,打开浏览器
(1)查看消息收发
切换到菜单管理:
再次切换到user页面,会断开之前的连接重新建立连接,因为vue每次进入页面都会执行mounted。
idea控制台打印:
(2)postman调用user发送消息接口
浏览器接收成功:
postman调用menu发送消息接口:
浏览器接收成功:
二、按用户推送
上面的demo,所有的客户端都会接收到消息,现在希望推送给某个user。则需要建立WebSocketSession和userId的关系,
(1)前端在open建立连接时发送userId;
<!DOCTYPE html>
<html>
<head><title>WebSocket Client</title>
</head>
<body><script>const userId = "user123"; // Replace with dynamic user IDconst socket = new WebSocket(`ws://localhost:8080/websocket-endpoint`);socket.onopen = function(event) {console.log("WebSocket connection opened.");socket.send(JSON.stringify({ type: "REGISTER", userId: userId }));};socket.onmessage = function(event) {const message = event.data;console.log("Message from server: " + message);};socket.onclose = function(event) {console.log("WebSocket connection closed.");};socket.onerror = function(error) {console.error("WebSocket Error: " + error);};</script>
</body>
</html>
(2)后端WebSocketHandler存储userId和WebSocketSession的映射关系
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;
import org.springframework.web.socket.CloseStatus;
import org.springframework.stereotype.Component;import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;@Component
public class UserWebSocketHandler extends TextWebSocketHandler {private final ConcurrentMap<String, WebSocketSession> userSessions = new ConcurrentHashMap<>();@Overridepublic void afterConnectionEstablished(WebSocketSession session) throws Exception {String userId = getUserIdFromSession(session);if (userId != null) {userSessions.put(userId, session);System.out.println("WebSocket connection established for userId: " + userId);}}@Overridepublic void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {String userId = getUserIdFromSession(session);if (userId != null) {userSessions.remove(userId);System.out.println("WebSocket connection closed for userId: " + userId);}}public void sendMessageToUser(String userId, String message) throws IOException {WebSocketSession session = userSessions.get(userId);if (session != null && session.isOpen()) {session.sendMessage(new TextMessage(message));}}private String getUserIdFromSession(WebSocketSession session) {// Assuming userId is stored as a session attributereturn (String) session.getAttributes().get("userId");}
}
(3)发送消息时根据userId检索目标WebSocketSession。
1、完整代码如下
1.1、前端
<template><div>this is user manage:{{this.userAccount}}</div>
</template>
<script>
export default {data() {return {socket: null,message: '',inputMessage: ''};},
computed: {userAccount() {return this.$store.getters.name;}
},mounted() {// Create a new WebSocket connectionthis.socket = new WebSocket("ws://localhost:2222/securityDemo/user-websocket-endpoint");// Set up event listenersthis.socket.onopen = (event) => {console.log('WebSocket connection opened.');let msg = {"userAccount":this.userAccount}this.socket.send(JSON.stringify(msg));};this.socket.onmessage = (event) => {this.message = event.data;};this.socket.onerror = (error) => {console.error('WebSocket Error:', error);};this.socket.onclose = (event) => {console.log('WebSocket connection closed.');};},methods: {sendMessage() {if (this.socket && this.inputMessage) {this.socket.send(this.inputMessage);this.inputMessage = ''; // Clear the input field after sending}}},// beforeDestroy() {// // Close the WebSocket connection when the component is destroyed// if (this.socket) {// this.socket.close();// }// }
};
</script>
1.2、后端:
(1)常量
package com.demo.security.ws.constant;import lombok.Getter;
import org.springframework.web.socket.WebSocketSession;import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;public class UserSessionConstant {@Getterprivate static Map<String,WebSocketSession> sessionMap = new HashMap<>();public static void add(String userId,WebSocketSession session) {sessionMap.put(userId,session);}public static void remove(WebSocketSession session) {//从map中找到key,再remove key//sessionMap.remove(userAccount);}
}
(2)ws操作
package com.demo.security.ws;import com.demo.security.dto.MsgDTO;
import com.demo.security.dto.UserDTO;
import com.demo.security.ws.constant.UserSessionConstant;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;import java.io.IOException;@Component
@Slf4j
public class UserWebSocketHandler extends TextWebSocketHandler {@Overridepublic void afterConnectionEstablished(WebSocketSession session) throws Exception {//UserSessionConstant.add(session);log.info("user有新的连接,sessionId:{}",session.getId());}@Overridepublic void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {// 处理接收到的消息log.info("user服务端Received message: {}",message.getPayload());String payLoad = message.getPayload();ObjectMapper objectMapper = new ObjectMapper();MsgDTO msgDTO = objectMapper.readValue(payLoad, MsgDTO.class);UserSessionConstant.add(msgDTO.getUserAccount(),session);}@Overridepublic void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {UserSessionConstant.remove(session);log.info("user连接已断开sessionId:{}",session.getId());}public void sendMessageToAll(String message) {for(WebSocketSession session : UserSessionConstant.getSessionMap().values()) {if (session.isOpen()) {try {session.sendMessage(new TextMessage(message));log.info("user发送消息给{}成功",session.getId());} catch (IOException e) {e.printStackTrace();}}}}public void sendMessageToUser(String message,String userAccount) {for(String sessionUserAccount : UserSessionConstant.getSessionMap().keySet()){if(!userAccount.equals(sessionUserAccount)){continue;}WebSocketSession session = UserSessionConstant.getSessionMap().get(userAccount);if (session.isOpen()) {try {session.sendMessage(new TextMessage(message));log.info("user发送消息给用户{}成功",userAccount);} catch (IOException e) {e.printStackTrace();}}}}
}
(3)controller接口
package com.demo.security.controller;import com.demo.security.dto.UserDTO;
import com.demo.security.ws.UserWebSocketHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
@RequestMapping("/userMsg")
public class UserMsgController {@Autowiredprivate UserWebSocketHandler userWebSocketHandler;@RequestMapping("/send")public void sendMessage(UserDTO messageDTO) {userWebSocketHandler.sendMessageToUser(messageDTO.toString(),messageDTO.getUserAccount());}
}
2、测试
(1)
打开两个浏览器,分别登录zs、admin
后端断点可以看到map存储了两条数据:
(2)调用postman接口给zs发送消息:
查看浏览器,zs的账号接收到了消息,admin的没有接收到: