要将 token 传递给 WebSocket,有以下几种常见方法:
方法一:在 WebSocket URL 中添加 token 参数(推荐)
修改 WebSocket URL,将 token 作为查询参数传递:
javascript
// 在创建 WebSocket 连接时,将 token 添加到 URL 中
const token = 'your-token-here'; // 从你的 auth 模块获取 token
ws.value = new WebSocket(`${wsUrl.value}?token=${encodeURIComponent(token)}`);
完整的修改示例:
javascript
const initWebSocket = () => {
if (ws.value && ws.value.readyState === WebSocket.OPEN) {
ws.value.close();
}
// 获取 token(从你的 auth 模块获取)
const token = getToken(); // 假设你有 getToken() 函数
try {
// 将 token 添加到 URL 中
const fullWsUrl = `${wsUrl.value}?token=${encodeURIComponent(token)}`;
ws.value = new WebSocket(fullWsUrl);
console.log('ws', ws.value);
ws.value.onopen = () => {
console.log('WebSocket 连接成功');
};
// ... 其他事件处理代码保持不变
} catch (err) {
console.log('initWebSocket error', err);
}
};
方法二:通过 HTTP 头部传递(需要服务器支持)
如果服务器支持通过 HTTP 头部验证,可以使用以下方式(需要使用 ReconnectingWebSocket 库或自定义实现):
javascript
// 需要先安装:npm install reconnecting-websocket
import ReconnectingWebSocket from 'reconnecting-websocket';
const token = getToken();
const rws = new ReconnectingWebSocket(wsUrl.value, [], {
connectionTimeout: 10000,
maxRetries: 3,
headers: {
'Authorization': `Bearer ${token}`,
// 或者
'X-Auth-Token': token
}
});
方法三:连接建立后立即发送认证消息
在 WebSocket 连接建立后,立即发送包含 token 的认证消息:
javascript
ws.value.onopen = () => {
console.log('WebSocket 连接成功');
// 连接建立后立即发送认证消息
const token = getToken();
const authMessage = {
type: 'auth',
token: token
};
ws.value.send(JSON.stringify(authMessage));
};
推荐实现
结合代码,推荐使用方法一,因为实现简单且服务器通常支持:
javascript
const initWebSocket = () => {
if (ws.value && ws.value.readyState === WebSocket.OPEN) {
ws.value.close();
}
try {
// 从你的 auth 模块获取 token
const token = getToken();
// 将 token 添加到 WebSocket URL 中
const fullWsUrl = token
? `${wsUrl.value}?token=${encodeURIComponent(token)}`
: wsUrl.value;
ws.value = new WebSocket(fullWsUrl);
console.log('ws', ws.value);
ws.value.onopen = () => {
console.log('WebSocket 连接成功');
};
ws.value.onmessage = (e) => {
// ... 你现有的消息处理代码
};
ws.value.onclose = () => {
console.log('连接已关闭');
};
} catch (err) {
console.log('initWebSocket error', err);
}
};
注意事项
- URL 编码 :使用
encodeURIComponent()确保 token 中的特殊字符被正确编码 - 安全性:URL 中的 token 可能会被记录在服务器日志中,如果安全性要求很高,建议使用方法三
- 服务器支持:确保你的后端服务器能够从 URL 参数中读取并验证 token
验证是否成功
你可以在浏览器开发者工具的 Network 标签中查看 WebSocket 连接,确认 URL 中包含了 token 参数。
这样修改后,你的 WebSocket 连接就会携带 token,服务器端可以根据这个 token 进行身份验证。
扩展:
token不放在url,放在header头部,Authorization Bearer + token
由于 WebSocket 不支持直接设置 HTTP 头部,以下是几种解决方案:
方法一:连接建立后立即发送认证消息(推荐)
这是最常用和安全的方式,不会暴露 token 在 URL 中:
javascript
const initWebSocket = () => {
if (ws.value && ws.value.readyState === WebSocket.OPEN) {
ws.value.close();
}
const token = `Bearer eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6ImZjZTFjZWQ5LTM0ODYtNGQ5Yy05Y2Q4LTI2YzliMzI0MWFjYyJ9.PVvyxvNINiucOZuFadHMaL-7K7hwzeQX9aVpAciNRLMyLGcF7ajKZ3nhKUM6v0rXY8E552_UnCOjp6EA5HrsWw`;
try {
// 不在 URL 中传递 token
ws.value = new WebSocket(wsUrl.value);
console.log('ws', ws.value);
ws.value.onopen = () => {
console.log('WebSocket 连接成功');
// 连接建立后立即发送认证消息
const authMessage = {
type: 'auth',
token: token
};
ws.value.send(JSON.stringify(authMessage));
};
ws.value.onmessage = (e) => {
// 检查是否是认证响应
if (e.data && typeof e.data === 'string') {
try {
const res = JSON.parse(e.data);
if (res.type === 'auth_response') {
if (res.success) {
console.log('WebSocket 认证成功');
} else {
console.error('WebSocket 认证失败:', res.message);
ws.value.close();
}
return;
}
} catch (error) {
// 如果不是 JSON 格式的消息,继续处理其他消息
}
}
// 你现有的消息处理代码
if (e.data === 'DONE') {
console.log('结束信号');
ws.value.close();
return;
}
// ... 其他消息处理逻辑保持不变
const res = JSON.parse(e.data);
// ... 你的现有处理逻辑
};
ws.value.onclose = () => {
console.log('连接已关闭');
};
ws.value.onerror = (error) => {
console.error('WebSocket 错误:', error);
};
} catch (err) {
console.log('initWebSocket error', err);
}
};
方法二:使用第三方库(需要安装)
如果你的服务器支持,可以使用 reconnecting-websocket 库:
javascript
npm install reconnecting-websocket
javascript
import ReconnectingWebSocket from 'reconnecting-websocket';
const initWebSocket = () => {
if (ws.value) {
ws.value.close();
}
const token = `Bearer eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6ImZjZTFjZWQ5LTM0ODYtNGQ5Yy05Y2Q4LTI2YzliMzI0MWFjYyJ9.PVvyxvNINiucOZuFadHMaL-7K7hwzeQX9aVpAciNRLMyLGcF7ajKZ3nhKUM6v0rXY8E552_UnCOjp6EA5HrsWw`;
const rws = new ReconnectingWebSocket(wsUrl.value, [], {
connectionTimeout: 10000,
maxRetries: 3,
WebSocket: class extends WebSocket {
constructor(url) {
super(url);
// 设置自定义头部(需要服务器支持)
this.protocol = token; // 这个方式可能不被所有服务器支持
}
}
});
ws.value = rws;
// ... 其他事件处理保持不变
};
方法三:使用你的 auth 模块获取 token
从你的代码中看到你有 getToken 函数,建议使用它:
javascript
import { getToken } from '@/package/utils/auth';
const initWebSocket = () => {
if (ws.value && ws.value.readyState === WebSocket.OPEN) {
ws.value.close();
}
// 从 auth 模块获取 token
const token = getToken();
if (!token) {
console.error('未获取到 token,无法建立 WebSocket 连接');
return;
}
const fullToken = `Bearer ${token}`;
try {
ws.value = new WebSocket(wsUrl.value);
console.log('ws', ws.value);
ws.value.onopen = () => {
console.log('WebSocket 连接成功');
// 发送认证消息
const authMessage = {
type: 'auth',
token: fullToken
};
ws.value.send(JSON.stringify(authMessage));
};
// ... 其他处理逻辑保持不变
} catch (err) {
console.log('initWebSocket error', err);
}
};
完整的推荐实现
javascript
import { ref, reactive, watch, onUnmounted, onMounted, nextTick } from 'vue';
import { CircleCloseFilled, Top } from '@element-plus/icons-vue';
import { BubbleList, Sender, Typewriter } from 'vue-element-plus-x';
import { getToken } from '@/package/utils/auth'; // 导入你的 auth 模块
// ... 其他代码保持不变
const initWebSocket = () => {
if (ws.value && ws.value.readyState === WebSocket.OPEN) {
ws.value.close();
}
// 从 auth 模块获取 token
const token = getToken();
if (!token) {
console.error('未获取到 token,无法建立 WebSocket 连接');
return;
}
const fullToken = `Bearer ${token}`;
try {
ws.value = new WebSocket(wsUrl.value);
console.log('ws', ws.value);
ws.value.onopen = () => {
console.log('WebSocket 连接成功');
// 发送认证消息
const authMessage = {
type: 'auth',
token: fullToken
};
ws.value.send(JSON.stringify(authMessage));
};
ws.value.onmessage = (e) => {
// 首先检查是否是认证响应
if (e.data && typeof e.data === 'string') {
try {
const res = JSON.parse(e.data);
// 如果是认证响应
if (res.type === 'auth_response') {
if (res.success) {
console.log('WebSocket 认证成功');
} else {
console.error('WebSocket 认证失败:', res.message);
ws.value.close();
}
return;
}
} catch (error) {
// 如果不是 JSON 格式的消息,继续处理其他消息
}
}
// 原有的消息处理逻辑
if (e.data === 'DONE') {
console.log('结束信号');
ws.value.close();
return;
}
console.log('接收data:', e.data);
const res = JSON.parse(e.data);
const msgIndex = res.index;
if (msgIndex >= 0 && msgIndex < dataList.value.length) {
const message = dataList.value[msgIndex];
console.log('message', message);
if (message) {
switch (res.type) {
case 'ai_response':
message.aiAnswer += res.content;
message.status = 'done';
break;
case 'ai_stream':
message.aiAnswer += res.chunk;
message.status = 'streaming';
break;
case 'error':
message.aiAnswer = `[Error] ${res.msg}`;
message.status = 'error';
break;
}
}
}
nextTick(() => {
scrollToBottom();
});
console.log('dataList.value', dataList.value);
};
ws.value.onclose = () => {
console.log('连接已关闭');
};
ws.value.onerror = (error) => {
console.error('WebSocket 错误:', error);
};
} catch (err) {
console.log('initWebSocket error', err);
}
};
// ... 其他代码保持不变
重要提醒
- 服务器支持:确保你的后端 WebSocket 服务器支持接收认证消息并验证 token
- 安全考虑:使用认证消息的方式比在 URL 中传递 token 更安全,因为不会在服务器日志中暴露 token
- 错误处理:添加认证失败的处理逻辑,及时关闭连接避免资源浪费
这是处理 WebSocket 认证的最佳实践方式。