### WebSocket连接问题:localhost可用但IP地址不可用的解决方案
#### 问题现象
- 使用 `ws://localhost:8088` 可以正常建立WebSocket连接
- 改用 `ws://192.168.1.120:8088`(服务器实际IP)时连接失败
- 主要表现:无法获取session信息
#### 根本原因
1. **同源策略限制**:浏览器要求建立WebSocket连接的地址必须与页面访问地址一致(协议+域名+端口)
2. **localhost与IP地址的区别**:
- 当页面通过 `http://localhost` 访问时,WebSocket连接也必须使用localhost
- 当页面通过IP地址访问时,WebSocket连接也必须使用相同IP地址
3. **跨设备访问问题**:如果其他设备访问该页面,前端代码中的localhost会被解析为访问者自己的本地地址
#### 解决方案
1. **统一访问地址**:
```javascript
// 修改前(仅本地开发可用)
new WebSocket("ws://localhost:80/chat");
// 修改后(支持IP访问)
new WebSocket("ws://192.xxx.xx.xx:80/chat");
```
2. **访问方式调整**:
- 确保浏览器通过IP地址访问页面:
```bash
http://192.xxx.xx.xx:80/main.html
```
3. **服务重启**:
- 修改后需要重启SpringBoot服务或前端服务
#### 技术原理
1. WebSocket连接建立过程:
- 浏览器首先发送HTTP请求(包含`Upgrade: websocket`头)
- 服务端响应101状态码完成协议升级
2. 地址一致性要求:
- 浏览器会检查WebSocket连接地址是否与页面同源
- 非一致地址可能导致连接被拒绝或session无法保持
#### 补充说明
- 生产环境建议使用域名而非IP地址
- 如需跨域访问,需要在服务端配置CORS:
```java
// Spring Boot示例
@Configuration
public class WebSocketConfig implements WebSocketConfigurer {
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(myHandler(), "/chat")
.setAllowedOrigins("*");
}
}
```
#### 最佳实践建议
1. 开发环境:
- 使用环境变量动态设置WebSocket地址
```javascript
const wsUrl = process.env.NODE_ENV === 'development'
? 'ws://localhost:8088/chat'
: 'ws://production-domain.com/chat';
new WebSocket(wsUrl);
```
2. 生产环境:
- 使用Nginx反向代理统一域名
- 启用WSS(WebSocket Secure)加密连接
通过以上调整,可以确保WebSocket连接在不同网络环境下都能正常建立。
# 补充说明
### **同源策略(Same-Origin Policy)详解**
#### **1. 什么是同源策略?**
同源策略是浏览器的一种核心安全机制,它限制了一个源(Origin)的文档或脚本如何与另一个源的资源进行交互。目的是防止恶意网站窃取用户数据或进行跨站攻击(如CSRF、XSS)。
---
#### **2. 如何判断“同源”?**
两个URL的**协议(Protocol)、域名(Host)、端口(Port)**必须完全相同,才属于同源。
| URL1 | URL2 | 是否同源 | 原因 |
|------|------|---------|------|
| `http://example.com/page` | `http://example.com/other` | ✅ 同源 | 协议、域名、端口相同 |
| `https://example.com/page` | `http://example.com/page` | ❌ 不同源 | 协议不同(HTTPS vs HTTP) |
| `http://example.com:80/page` | `http://example.com:8080/page` | ❌ 不同源 | 端口不同(80 vs 8080) |
| `http://sub.example.com/page` | `http://example.com/page` | ❌ 不同源 | 子域名不同(sub.example.com vs example.com) |
---
#### **3. 同源策略如何影响WebSocket?**
浏览器在建立WebSocket连接时,会检查:
- **当前网页的源(Origin)**(即浏览器地址栏的URL)
- **WebSocket请求的目标地址**(`ws://` 或 `wss://`)
**如果两者不同源,浏览器可能会阻止连接,除非服务器明确允许跨域。**
---
#### **4. 为什么 `localhost` 能用,但 `IP` 不行?**
- **情况1**:前端页面通过 `http://localhost:8080` 访问,但WebSocket连接 `ws://192.168.1.120:8088`
- ❌ **不同源**(`localhost` ≠ `192.168.1.120`,且端口不同)
- 浏览器会拒绝连接,导致无法获取Session。
- **情况2**:前端页面通过 `http://192.168.1.120:8080` 访问,WebSocket连接 `ws://192.168.1.120:8088`
- ❌ **仍然不同源**(端口不同,8080 ≠ 8088)
- 除非服务器允许跨域,否则连接失败。
---
#### **5. 如何解决?**
##### **方法1:保持同源(推荐)**
- **前端页面和WebSocket使用相同的协议、域名、端口**:
```javascript
// 如果页面是 http://192.168.1.120:8080/index.html
new WebSocket("ws://192.168.1.120:8080/chat"); // 端口一致
```
##### **方法2:服务器允许跨域(CORS)**
- **Spring Boot 配置允许跨域WebSocket**:
```java
@Configuration
public class WebSocketConfig implements WebSocketConfigurer {
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(myHandler(), "/chat")
.setAllowedOrigins("*"); // 允许所有来源
}
}
```
##### **方法3:Nginx反向代理(生产环境推荐)**
- 让前端和WebSocket都通过同一个域名访问:
```nginx
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://frontend_server;
}
location /chat {
proxy_pass http://websocket_server;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
}
}
```
这样,前端访问 `http://example.com`,WebSocket连接 `ws://example.com/chat`,**保持同源**。
---
#### **6. 总结**
| 问题 | 原因 | 解决方案 |
|------|------|---------|
| `localhost` 能连,`IP` 不能连 | 浏览器同源策略限制 | 确保WebSocket地址与页面访问地址一致 |
| 跨设备访问失败 | 前端代码写死 `localhost` | 动态获取当前主机IP或使用域名 |
| Session 丢失 | 不同源导致Cookie/Session不共享 | 配置CORS或使用反向代理统一域名 |
**关键点:浏览器要求WebSocket连接的源必须与页面URL一致,否则可能被拦截!**