在之前搭建好的项目的基础上新版security demo(二)前端-CSDN博客
目录
一、代码改造
1、后端改造
2、VUE使用websocket
3、测试
二、按用户推送
1、完整代码如下
1.1、前端
1.2、后端:
2、测试
一、代码改造
1、后端改造
(1)把websocket相关代码复制到web项目:
(2)添加白名单
web-socket端口无需token验证,SecurityWebConfig添加白名单
@Bean
public 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 connection
this.socket = new WebSocket("ws://localhost:2222/securityDemo/user-websocket-endpoint");
// Set up event listeners
this.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 ID
const 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<>();
@Override
public 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);
}
}
@Override
public 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 attribute
return (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 connection
this.socket = new WebSocket("ws://localhost:2222/securityDemo/user-websocket-endpoint");
// Set up event listeners
this.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 {
@Getter
private 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 {
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
//UserSessionConstant.add(session);
log.info("user有新的连接,sessionId:{}",session.getId());
}
@Override
public 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);
}
@Override
public 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 {
@Autowired
private 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的没有接收到: