diff --git a/backend/src/main/java/com/openisle/config/SecurityConfig.java b/backend/src/main/java/com/openisle/config/SecurityConfig.java index 5fca385de..59218682a 100644 --- a/backend/src/main/java/com/openisle/config/SecurityConfig.java +++ b/backend/src/main/java/com/openisle/config/SecurityConfig.java @@ -104,7 +104,7 @@ public class SecurityConfig { .exceptionHandling(eh -> eh.accessDeniedHandler(customAccessDeniedHandler)) .authorizeHttpRequests(auth -> auth .requestMatchers(HttpMethod.OPTIONS, "/**").permitAll() - .requestMatchers("/ws/**").permitAll() + .requestMatchers("/api/ws/**").permitAll() .requestMatchers(HttpMethod.POST, "/api/auth/**").permitAll() .requestMatchers(HttpMethod.GET, "/api/posts/**").permitAll() .requestMatchers(HttpMethod.GET, "/api/comments/**").permitAll() @@ -173,7 +173,7 @@ public class SecurityConfig { response.getWriter().write("{\"error\": \"Invalid or expired token\"}"); return; } - } else if (!uri.startsWith("/api/auth") && !publicGet && !uri.startsWith("/ws")) { + } else if (!uri.startsWith("/api/auth") && !publicGet && !uri.startsWith("/api/ws")) { response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); response.setContentType("application/json"); response.getWriter().write("{\"error\": \"Missing token\"}"); diff --git a/backend/src/main/java/com/openisle/config/WebSocketConfig.java b/backend/src/main/java/com/openisle/config/WebSocketConfig.java index 254ead4e2..6ae1d580c 100644 --- a/backend/src/main/java/com/openisle/config/WebSocketConfig.java +++ b/backend/src/main/java/com/openisle/config/WebSocketConfig.java @@ -41,8 +41,7 @@ public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { @Override public void registerStompEndpoints(StompEndpointRegistry registry) { - // Registers the "/ws" endpoint, enabling SockJS fallback options so that alternate transports may be used if WebSocket is not available. - registry.addEndpoint("/ws") + registry.addEndpoint("/api/ws") // 安全改进:使用具体的允许源,而不是通配符 .setAllowedOrigins( "http://127.0.0.1:8080", diff --git a/frontend_nuxt/composables/useWebSocket.js b/frontend_nuxt/composables/useWebSocket.js index 389af3f1a..5b606245b 100644 --- a/frontend_nuxt/composables/useWebSocket.js +++ b/frontend_nuxt/composables/useWebSocket.js @@ -1,85 +1,83 @@ -import { ref } from 'vue'; -import { Client } from '@stomp/stompjs'; -import SockJS from 'sockjs-client/dist/sockjs.min.js'; -import { useRuntimeConfig } from '#app'; +import { ref } from 'vue' +import { Client } from '@stomp/stompjs' +import SockJS from 'sockjs-client/dist/sockjs.min.js' +import { useRuntimeConfig } from '#app' -const client = ref(null); -const isConnected = ref(false); +const client = ref(null) +const isConnected = ref(false) const connect = (token) => { - if (isConnected.value) { - return; - } + if (isConnected.value) { + return + } - const config = useRuntimeConfig(); - const API_BASE_URL = config.public.apiBaseUrl; - const socketUrl = `${API_BASE_URL}/ws`; + const config = useRuntimeConfig() + const API_BASE_URL = config.public.apiBaseUrl + const socketUrl = `${API_BASE_URL}/api/ws` - const socket = new SockJS(socketUrl); - const stompClient = new Client({ - webSocketFactory: () => socket, - connectHeaders: { - Authorization: `Bearer ${token}`, - }, - debug: function (str) { - }, - reconnectDelay: 5000, - heartbeatIncoming: 4000, - heartbeatOutgoing: 4000, - }); + const socket = new SockJS(socketUrl) + const stompClient = new Client({ + webSocketFactory: () => socket, + connectHeaders: { + Authorization: `Bearer ${token}`, + }, + debug: function (str) {}, + reconnectDelay: 5000, + heartbeatIncoming: 4000, + heartbeatOutgoing: 4000, + }) - stompClient.onConnect = (frame) => { - isConnected.value = true; - }; + stompClient.onConnect = (frame) => { + isConnected.value = true + } - stompClient.onStompError = (frame) => { - console.error('WebSocket STOMP error:', frame); - }; + stompClient.onStompError = (frame) => { + console.error('WebSocket STOMP error:', frame) + } - stompClient.activate(); - client.value = stompClient; -}; + stompClient.activate() + client.value = stompClient +} const disconnect = () => { - if (client.value) { - isConnected.value = false; - client.value.deactivate(); - client.value = null; - } -}; + if (client.value) { + isConnected.value = false + client.value.deactivate() + client.value = null + } +} const subscribe = (destination, callback) => { - - if (!isConnected.value || !client.value || !client.value.connected) { - return null; - } + if (!isConnected.value || !client.value || !client.value.connected) { + return null + } - try { - const subscription = client.value.subscribe(destination, (message) => { - try { - if (destination.includes('/queue/unread-count')) { - callback(message); - } else { - const parsedMessage = JSON.parse(message.body); - callback(parsedMessage); - } - } catch (error) { - callback(message); - } - }); - - return subscription; - } catch (error) { - return null; - } -}; + try { + const subscription = client.value.subscribe(destination, (message) => { + try { + if (destination.includes('/queue/unread-count')) { + callback(message) + } else { + const parsedMessage = JSON.parse(message.body) + callback(parsedMessage) + } + } catch (error) { + callback(message) + } + }) + + return subscription + } catch (error) { + return null + } +} export function useWebSocket() { - return { - client, - isConnected, - connect, - disconnect, - subscribe, - }; -} \ No newline at end of file + return { + client, + isConnected, + connect, + disconnect, + subscribe, + } +} diff --git a/frontend_nuxt/pages/message-box/[id].vue b/frontend_nuxt/pages/message-box/[id].vue index 617f2ae17..af0086974 100644 --- a/frontend_nuxt/pages/message-box/[id].vue +++ b/frontend_nuxt/pages/message-box/[id].vue @@ -281,7 +281,13 @@ watch(isConnected, (newValue) => { subscription = subscribe(`/topic/conversation/${conversationId}`, (message) => { // 避免重复显示当前用户发送的消息 if (message.sender.id !== currentUser.value.id) { - messages.value.push(message) + messages.value.push({ + ...message, + src: message.sender.avatar, + iconClick: () => { + navigateTo(`/users/${message.sender.id}`, { replace: true }) + }, + }) // 实时收到消息时自动标记为已读 markConversationAsRead() setTimeout(() => {