Merge pull request #697 from nagisa77/feature/daily_bugfix_0822_b

fix: 修复nginx /ws拦截问题
This commit is contained in:
Tim
2025-08-22 15:27:10 +08:00
committed by GitHub
4 changed files with 78 additions and 75 deletions

View File

@@ -104,7 +104,7 @@ public class SecurityConfig {
.exceptionHandling(eh -> eh.accessDeniedHandler(customAccessDeniedHandler)) .exceptionHandling(eh -> eh.accessDeniedHandler(customAccessDeniedHandler))
.authorizeHttpRequests(auth -> auth .authorizeHttpRequests(auth -> auth
.requestMatchers(HttpMethod.OPTIONS, "/**").permitAll() .requestMatchers(HttpMethod.OPTIONS, "/**").permitAll()
.requestMatchers("/ws/**").permitAll() .requestMatchers("/api/ws/**").permitAll()
.requestMatchers(HttpMethod.POST, "/api/auth/**").permitAll() .requestMatchers(HttpMethod.POST, "/api/auth/**").permitAll()
.requestMatchers(HttpMethod.GET, "/api/posts/**").permitAll() .requestMatchers(HttpMethod.GET, "/api/posts/**").permitAll()
.requestMatchers(HttpMethod.GET, "/api/comments/**").permitAll() .requestMatchers(HttpMethod.GET, "/api/comments/**").permitAll()
@@ -173,7 +173,7 @@ public class SecurityConfig {
response.getWriter().write("{\"error\": \"Invalid or expired token\"}"); response.getWriter().write("{\"error\": \"Invalid or expired token\"}");
return; 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.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.setContentType("application/json"); response.setContentType("application/json");
response.getWriter().write("{\"error\": \"Missing token\"}"); response.getWriter().write("{\"error\": \"Missing token\"}");

View File

@@ -41,8 +41,7 @@ public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override @Override
public void registerStompEndpoints(StompEndpointRegistry registry) { 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("/api/ws")
registry.addEndpoint("/ws")
// 安全改进:使用具体的允许源,而不是通配符 // 安全改进:使用具体的允许源,而不是通配符
.setAllowedOrigins( .setAllowedOrigins(
"http://127.0.0.1:8080", "http://127.0.0.1:8080",

View File

@@ -1,85 +1,83 @@
import { ref } from 'vue'; import { ref } from 'vue'
import { Client } from '@stomp/stompjs'; import { Client } from '@stomp/stompjs'
import SockJS from 'sockjs-client/dist/sockjs.min.js'; import SockJS from 'sockjs-client/dist/sockjs.min.js'
import { useRuntimeConfig } from '#app'; import { useRuntimeConfig } from '#app'
const client = ref(null); const client = ref(null)
const isConnected = ref(false); const isConnected = ref(false)
const connect = (token) => { const connect = (token) => {
if (isConnected.value) { if (isConnected.value) {
return; return
} }
const config = useRuntimeConfig(); const config = useRuntimeConfig()
const API_BASE_URL = config.public.apiBaseUrl; const API_BASE_URL = config.public.apiBaseUrl
const socketUrl = `${API_BASE_URL}/ws`; const socketUrl = `${API_BASE_URL}/api/ws`
const socket = new SockJS(socketUrl); const socket = new SockJS(socketUrl)
const stompClient = new Client({ const stompClient = new Client({
webSocketFactory: () => socket, webSocketFactory: () => socket,
connectHeaders: { connectHeaders: {
Authorization: `Bearer ${token}`, Authorization: `Bearer ${token}`,
}, },
debug: function (str) { debug: function (str) {},
}, reconnectDelay: 5000,
reconnectDelay: 5000, heartbeatIncoming: 4000,
heartbeatIncoming: 4000, heartbeatOutgoing: 4000,
heartbeatOutgoing: 4000, })
});
stompClient.onConnect = (frame) => { stompClient.onConnect = (frame) => {
isConnected.value = true; isConnected.value = true
}; }
stompClient.onStompError = (frame) => { stompClient.onStompError = (frame) => {
console.error('WebSocket STOMP error:', frame); console.error('WebSocket STOMP error:', frame)
}; }
stompClient.activate(); stompClient.activate()
client.value = stompClient; client.value = stompClient
}; }
const disconnect = () => { const disconnect = () => {
if (client.value) { if (client.value) {
isConnected.value = false; isConnected.value = false
client.value.deactivate(); client.value.deactivate()
client.value = null; client.value = null
} }
}; }
const subscribe = (destination, callback) => { const subscribe = (destination, callback) => {
if (!isConnected.value || !client.value || !client.value.connected) {
if (!isConnected.value || !client.value || !client.value.connected) { return null
return null; }
}
try { try {
const subscription = client.value.subscribe(destination, (message) => { const subscription = client.value.subscribe(destination, (message) => {
try { try {
if (destination.includes('/queue/unread-count')) { if (destination.includes('/queue/unread-count')) {
callback(message); callback(message)
} else { } else {
const parsedMessage = JSON.parse(message.body); const parsedMessage = JSON.parse(message.body)
callback(parsedMessage); callback(parsedMessage)
} }
} catch (error) { } catch (error) {
callback(message); callback(message)
} }
}); })
return subscription; return subscription
} catch (error) { } catch (error) {
return null; return null
} }
}; }
export function useWebSocket() { export function useWebSocket() {
return { return {
client, client,
isConnected, isConnected,
connect, connect,
disconnect, disconnect,
subscribe, subscribe,
}; }
} }

View File

@@ -281,7 +281,13 @@ watch(isConnected, (newValue) => {
subscription = subscribe(`/topic/conversation/${conversationId}`, (message) => { subscription = subscribe(`/topic/conversation/${conversationId}`, (message) => {
// 避免重复显示当前用户发送的消息 // 避免重复显示当前用户发送的消息
if (message.sender.id !== currentUser.value.id) { 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() markConversationAsRead()
setTimeout(() => { setTimeout(() => {