省流:在SpringSecurity的过滤链配置中放行/chatSever连接路径
1. 先介绍一下WebSocket 客户端与服务器之间的连接过程:
WebSocket 连接是基于 HTTP 协议建立的。WebSocket 客户端与服务器之间的连接先通过 HTTP 协议进行“握手”,然后升级为 WebSocket 连接,之后可以进行双向数据交换。详细过程如下:
1)客户端发送一个 HTTP 请求,其中包含Upgrade: websocket和Connection: Upgrade等关键头部信息。
- Upgrade:告知服务器,客户端希望将连接协议从 HTTP 升级为 WebSocket。
- Connection:指示请求是要升级协议。
2)服务器收到客户端的请求后,会检查是否符合 WebSocket 协议的要求。如果符合条件,服务器会返回一个 HTTP 101 状态码,请求头中也会包含Connection: Upgrade等字段,表示协议已成功切换。
3)握手成功后,WebSocket 连接建立。此时,HTTP 协议的连接被升级为 WebSocket 协议。此后,客户端和服务器之间的通信不再使用传统的 HTTP 请求-响应模型,而是进入了双向通信模式,WebSocket 可以实时传输数据。
4)当任一方(客户端或服务器)希望结束连接时,WebSocket 协议允许发送一个 Close 帧来关闭连接。关闭的过程中,会有一方发送 Close
帧,另一方响应 Close
帧,之后双方关闭TCP连接。
2. 再说结论:
项目中使用了SpringSecurity机制对请求路径进行了身份认证和权限区分,而 Spring Security 默认的安全过滤器链会将 WebSocket 握手请求(一个通过 HTTP 升级到 WebSocket 协议的请求)当作普通的 HTTP 请求进行处理,从而阻止 WebSocket 连接的建立。
因此解决步骤如下:
在Security的过滤链配置中添加
.requestMatchers("/chatSever").permitAll() // 放行 WebSocket 连接请求
放行WebSocket 连接请求,即可解决该bug。
如果自定义了JWT认证过滤器,也应在里面放行该请求。
3. 排查思路:
1. 使用postman向websocket服务器发送连接请求:
ws://localhost:8090/chatSever
2. 发现结果如下:
图片表明,客户端尝试连接websocket服务器,先发送了一个握手请求(HTTP协议),但服务器没有返回状态101,而是状态200。结合响应的头部信息,我们可以推断出服务器对WebSocket 握手请求处理错误,进一步可以定位到SpringSecurity。
那么,既然WebSocket 握手请求被当成了普通HTTP请求,为什么返回的不是401而是200呢?
可能是WebSocket 握手请求由于其特殊性(协议升级请求),Spring Security 不会明确触发身份验证逻辑,而是返回 `200`。(具体行为还应阅读源码)