Unity WebGL端Spring WebSocket连接异常排查求助
Spring WebSocket与Unity WebGL跨域连接问题排查
问题背景
- Spring WebSocket服务在Postman、Unity编辑器中均可正常连接
- Unity打包为WebGL后,无法连接ngrok提供的
wss://unbiased-evenly-worm.ngrok-free.app/game地址,但能正常连接本地ws://localhost:8080/game - ngrok的wss地址可被Postman正常连接,怀疑是CORS配置问题
相关代码
Unity WebGL连接代码
async void Start() { Debug.Log("Starting WebSocket connection..."); clientId = System.Guid.NewGuid().ToString(); ws = new WebSocket("wss://unbiased-evenly-worm.ngrok-free.app/game"); ws.OnOpen += () => { Debug.Log("WebSocket connected!"); }; ws.OnMessage += (byte[] msg) => { string message = Encoding.UTF8.GetString(msg); Debug.Log("Message received: " + message); OnMessageReceived(message); }; ws.OnError += (string errMsg) => { Debug.LogError("WebSocket error: " + errMsg); }; ws.OnClose += (WebSocketCloseCode code) => { Debug.LogWarning("WebSocket closed with code: " + code.ToString()); }; Debug.Log("Attempting to connect WebSocket..."); try { await ws.Connect(); Debug.Log("WebSocket connection successful."); } catch (System.Exception ex) { Debug.LogError("WebSocket connection failed: " + ex.Message); } targetPosition = transform.position; Cursor.lockState = CursorLockMode.Locked; Cursor.visible = false; }
Spring 配置代码
@Configuration @EnableWebMvc public class CorsConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOriginPatterns("*") .allowedMethods("GET", "POST", "PUT", "DELETE") .allowedHeaders("*") .allowCredentials(true) .maxAge(3600); } } @Configuration @EnableWebSecurity public class SecurityConfig { private final JWTUtil jwtUtil; public SecurityConfig(JWTUtil jwtUtil) { this.jwtUtil = jwtUtil; } @Bean public BCryptPasswordEncoder bCryptPasswordEncoder() { return new BCryptPasswordEncoder(); } @Bean public WebSecurityCustomizer webSecurityCustomizer() { return web -> web.ignoring() .requestMatchers("/error", "/favicon.ico"); } @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .csrf(csrf -> csrf.disable()) .formLogin(formLogin -> formLogin.disable()) .httpBasic(httpBasic -> httpBasic.disable()) .authorizeHttpRequests(auth -> auth .requestMatchers("/login", "/", "/join", "/verify-id", "/register", "/check-email", "/verify-email", "/verify-test", "/oauth/google", "/oauth/kakao", "/oauth","/profile/**","/refresh", "/reset-password","/reset-password-auth","/game/**","upload","/image","/game").permitAll() .anyRequest().authenticated() ) .addFilterBefore(new JWTFilter(jwtUtil), UsernamePasswordAuthenticationFilter.class) .sessionManagement(session -> session .sessionCreationPolicy(SessionCreationPolicy.STATELESS) ); return http.build(); } } @Configuration @EnableWebSocket public class WebSocketConfig implements WebSocketConfigurer { @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { registry.addHandler(new GameWebSocketHandler(), "/game") .setAllowedOrigins("*"); } }
问题分析与解决方案
核心问题点
- WebSocket的CORS配置独立于普通HTTP:你配置的
CorsConfig只对普通HTTP请求生效,WebSocket的握手请求(HTTP GET升级请求)受WebSocketConfig中的跨域配置控制。 setAllowedOrigins("*")与凭证兼容性问题:Spring Boot 2.4+后,setAllowedOrigins("*")在搭配allowCredentials(true)时会违反CORS规范,导致浏览器拒绝连接。WebGL在浏览器环境下默认会携带凭证,触发了这个冲突。
具体修复步骤
1. 修改Spring WebSocket跨域配置
将WebSocketConfig中的setAllowedOrigins("*")替换为setAllowedOriginPatterns("*"),后者支持通配符且兼容凭证配置:
@Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { registry.addHandler(new GameWebSocketHandler(), "/game") .setAllowedOriginPatterns("*"); // 替代setAllowedOrigins,兼容CORS凭证规则 }
如果你的Unity WebGL部署在固定域名,建议指定具体源(如"https://your-webgl-domain.com"),既安全又避免潜在冲突。
2. 验证Spring Security放行规则
当前SecurityConfig已将/game加入permitAll,确保WebSocket握手请求不会被JWT拦截。可额外检查JWTFilter是否排除了/game路径,避免握手请求被强制验证token。
3. 浏览器调试定位
在WebGL运行的浏览器中打开开发者工具(F12),查看Network面板的WebSocket握手请求:
- 检查响应头是否包含
Access-Control-Allow-Origin,值是否匹配WebGL的部署域名 - 查看Console面板是否有明确的CORS错误提示,进一步定位问题
补充说明
- Postman不受浏览器同源策略限制,因此能正常连接ngrok地址;Unity编辑器运行时也并非严格遵循浏览器CORS规则,这就是本地测试正常但WebGL打包后失败的原因。
内容的提问来源于stack exchange,提问作者kim
相关产品推荐
相关产品推荐

