Angular 12对接Spring Boot WebSocket出现CORS跨域错误如何解决?
Spring Boot + Angular 12 WebSocket CORS问题及修复
问题背景
本地使用Spring Boot和Angular 12开发WebSocket功能,查阅大量教程后仍遇到CORS错误,其余普通REST接口跨域均正常。
前端代码
Angular端Socket连接代码如下,该代码在页面加载时初始化的组件中执行:
import { Injectable } from '@angular/core'; import { NotificationWebSocket } from '../model/NotificationWebSocket.model'; import { Stomp } from '@stomp/stompjs'; import * as SockJS from 'sockjs-client'; @Injectable({ providedIn: 'root' }) export class WebsocketService{ stompClient: any; webSocketEndPoint: string = 'http://localhost:8080/notification'; topic : string = "/notifications/messages" constructor(){ console.log("Initialize WebSocket Connection"); let ws = new SockJS(this.webSocketEndPoint); this.stompClient = Stomp.over(ws); const _this = this; _this.stompClient.connect({}, function (frame : any) { _this.stompClient.subscribe(_this.topic, function (sdkEvent : any) { _this.onMessageReceived(sdkEvent); }); },); } disconnect() { if (this.stompClient !== null) { this.stompClient.disconnect(); } console.log("Disconnected"); } onMessageReceived(message : NotificationWebSocket) { console.log("Message Recieved from Server :: " + message); } }
后端初始配置
WebSocket核心配置
@Configuration @EnableWebSocketMessageBroker public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { @Override public void configureMessageBroker(MessageBrokerRegistry config) { config.enableSimpleBroker("/notification"); config.setApplicationDestinationPrefixes("/wigstat"); } @Override public void registerStompEndpoints(StompEndpointRegistry registry) { registry.addEndpoint("/notifications").setAllowedOrigins( "*" ).withSockJS(); } } @Controller public class WebSockerSender { @Autowired private NotificationWebSocketAdapter notificationWebSocketAdapter; @MessageMapping("/updates") @SendTo("/notifications/messages") public NotificationWebSocket send(Notification notification) { return notificationWebSocketAdapter.fromEntityToWebSocket( notification ); } }
通用REST接口CORS过滤器
@Component @Order(Ordered.HIGHEST_PRECEDENCE) public class CorsFilter implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletResponse res = (HttpServletResponse) response; HttpServletRequest req = (HttpServletRequest) request; res.setHeader( "Access-Control-Allow-Origin", "*" ); res.setHeader( "Access-Control-Allow-Methods", "GET, POST, DELETE, PUT, OPTIONS" ); res.setHeader( "Access-Control-Allow-Headers", "*" ); // 预检请求直接返回成功 if ( req.getMethod().equals("OPTIONS") ) { res.setHeader( "Access-Control-Max-Age", "86400" ); res.setStatus( HttpServletResponse.SC_OK ); return; } chain.doFilter( request, response ); } @Override public void destroy() { } @Override public void init(FilterConfig filterConfig) { } }
报错信息
运行前端后控制台报错如下:
第一次修复尝试
分别修改CORS过滤器和WebSocket配置的允许源为前端本地地址,仍报相同错误:
// 过滤器修改 res.setHeader( "Access-Control-Allow-Origin", "http://localhost:4200" );
// WebSocket配置修改 @Override public void registerStompEndpoints(StompEndpointRegistry registry) { registry.addEndpoint("/notifications").setAllowedOrigins( "http://localhost:4200" ).withSockJS(); }
最终有效修复方案
修改CORS过滤器逻辑,同时保持WebSocket配置的允许源为http://localhost:4200后问题解决:
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletResponse res = (HttpServletResponse) response; HttpServletRequest req = (HttpServletRequest) request; String origin = req.getHeader("Origin"); res.setHeader("Access-Control-Allow-Origin", allowedOrigins.contains(origin) ? origin : "*"); res.setHeader( "Access-Control-Allow-Credentials", "true" ); res.setHeader("Vary", "Origin"); res.setHeader( "Access-Control-Allow-Methods", "GET, POST, DELETE, PUT, OPTIONS" ); res.setHeader( "Access-Control-Allow-Headers", "*" ); // 预检请求直接返回成功 if ( req.getMethod().equals("OPTIONS") ) { res.setHeader( "Access-Control-Max-Age", "86400" ); res.setStatus( HttpServletResponse.SC_OK ); return; } chain.doFilter( request, response ); }
后续待优化问题
需要适配除本地开发环境外的所有不同部署环境的跨域配置。
内容的提问来源于stack exchange,提问作者Gabriel García Garrido
相关产品推荐
相关产品推荐

