WebSocket 403 主因是握手阶段被拦截,90%以上源于Spring拒绝Origin、Nginx丢Upgrade头、Spring Security拦截/ws路径;需用setAllowedOriginPatterns()替代已废弃的setAllowedOrigins(),显式放行路径并配置Nginx代理头。

WebSocket 403 不是权限问题,而是握手阶段被拦截——Spring 拒绝 Origin、Nginx 丢 Upgrade 头、Spring Security 提前拦掉 /ws 路径,三者占 90% 以上原因。直接配 setAllowedOrigins("*") 基本无效,尤其带凭据(withCredentials: true)时会直接报错。
Spring Boot 原生 WebSocket 的 allowedOrigins 配置为何失效?
新版 Spring(2.5+)已弃用 setAllowedOrigins("*"),且它对带凭据请求强制拒绝(浏览器规范要求不能返回 *)。更关键的是:该方法只在注册端点时生效,若没加 @EnableWebSocket 注解,整个配置类不会被扫描;若用了 SockJS,/ws/info 这个 HTTP 探测请求也得单独放行。
- Spring Boot 2.6+ 必须改用
setAllowedOriginPatterns("*"),否则启动报错 - 若前端 Origin 是
http://localhost:3000,后端收到的值必须精确匹配,"*"不参与字符串匹配 - 推荐显式列出可信源:
setAllowedOriginPatterns("http://localhost:3000", "https://yourdomain.com") - 如需动态校验(比如读配置或白名单库),必须实现
HandshakeInterceptor,在beforeHandshake中手动判断并设置响应头
Spring Security 拦截了 WebSocket 握手请求
哪怕 WebSocket 端点配对了,只要启用了 Spring Security,默认就会把 GET /ws 当作普通 HTTP 请求处理,并执行认证/授权逻辑,导致 403。SecurityFilterChain 不识别 WebSocket 协议升级语义,必须显式豁免路径。
- 在
WebSecurityCustomizer或WebSecurityConfigurerAdapter(旧版)中调用ignoring().requestMatchers("/ws/**") - 注意:必须是
/ws/**,不是/**,否则可能绕过其他安全控制 - 若用 SockJS,还需额外放行
/ws/info、/ws/xxx/websocket等衍生路径 - 不要只配全局 CORS 过滤器(
CorsConfiguration),它不作用于 WebSocket 握手流程
Nginx 反向代理下 WebSocket 返回 403 或 101 失败
本地直连正常、Nginx 后报 Error during WebSocket handshake: Unexpected response code: 403,基本可断定是代理头丢失。Nginx 默认不透传 Upgrade 和 Connection,后端收不到协议升级信号,只能当普通 HTTP 处理并拒掉。
- 必须在
location块中补全三要素:proxy_http_version 1.1、proxy_set_header Upgrade $http_upgrade、proxy_set_header Connection "$connection_upgrade" -
$connection_upgrade需提前在http块定义:map $http_upgrade $connection_upgrade { default upgrade; '' close; } - 禁止写
proxy_set_header Origin "",这会清空 Origin 头,导致后端跨域校验失败 - 检查 SELinux 或防火墙是否拦截了非标准端口(如 8080 上的 WebSocket 流量),CentOS 系统常见
STOMP over WebSocket 的跨域配置差异
STOMP 使用 StompEndpointRegistry 注册端点,其跨域控制逻辑与原生 WebSocket 独立,@CrossOrigin 或 WebMvc 的 CORS 配置完全不生效。错误地复用 HTTP 跨域方案,是新手最常踩的坑。
- 必须在
registerStompEndpoints中为每个addEndpoint()显式调用setAllowedOriginPatterns(...) - 若启用 SockJS,
withSockJS()之后的探测请求(如/ws/info)走的是 HTTP,但依然要受 STOMP 端点的 origin 控制 - 不要在网关(如 Spring Cloud Gateway)和后端同时配 CORS,重复的
Access-Control-Allow-Origin响应头会导致浏览器直接拒绝连接 - Gateway 的
globalcors对ws://或wss://协议无效,必须确保路由uri是lb:ws://...并匹配 Upgrade 请求
真正麻烦的从来不是“怎么配”,而是哪一层在拦——Nginx、Spring Security、WebSocket 端点注册、STOMP 配置,四层叠加时容易漏掉某一层的放行。建议按顺序排查:先看 Network 面板里握手请求的响应状态码和响应头,再逐层确认对应组件是否做了适配。尤其是 Origin 值和后端实际收到的是否一致,这个细节 70% 的人会忽略。

















