浏览器中WebSocket如何自定义请求头

3,487 阅读3分钟

前言

先说结论:浏览器中WebSocket不支持自定义请求头。

最近在项目中使用到了WebSocket。按照后端原先的设想:发送WebSocket请求时通过自定义请求头携带token等信息,后端鉴权后再进行对应的处理。但WebSocket Client并不支持自定义请求头,这一点可以从#MDN WebSocket API、网上的各种文档得出结论。

当我拿着结论告诉后端时,后端说为什么他可以通过Postman添加请求头呢?于是我便停止划水,在网上开始搜索起来🏄...

MDN中对WebSocket的介绍

WebSocket构造函数会创建一个webSocket对象,并立即建立WebSocket连接。它接收2个参数:

  • url: WebSocket链接,仅支持ws、wss、http、https协议,否则会报错
  • protocols(可选): 子协议名称

从参数可以看出,没有headers选项,也没有提供修改WebSocket请求头的方法。

为什么浏览器中WebSocket不支持自定义请求头呢?

在WHATWG websockets的issues中有一个提议:#Support for custom headers for handshake。得到了WHATWG成员和Websocket维护者的关注与回复,原因如下:

  1. WebSocket不是HTTP API。若允许WebSocket自定义请求头的话,需要发出CORS preflight请求。但在浏览器中,WebSocket是独立于fetch、XHR(有CORS的逻辑)实现的。如果需要支持自定义请求头的话,会有复杂的重构过程。
  2. WebSocket握手的安全模型与<img />标签类似,比<img />标签暴露更少能力,以防止请求伪造。(<img />标签也不支持自定义请求头😝)。
  3. 除了有限的子协议协商外,WebSocket握手并不是用于交换交换元数据的方式。非浏览器中的WebSocket可能会这样实现(难怪后端说他可以自定义请求头),但是并不建议这样做,而应该把它当成像TCP/IP/SYN数据包更底层的操作。
  4. 通过实现CORS preflight可以给WebSocket自定义请求头,但这样额外的实现、标准化、维护成本与其实用性不成正比。

不知道我理解是否有误,建议大家看下原文。

浏览器中WebSocket如何传递类似请求头的信息呢?

#ApiFox上有介绍4种解决的办法,我没有全部考证。我列举了我认为比较靠谱的2种(毕竟够用就行了,“回”字有四种写法😜):

  1. WebSocket的握手阶段使用HTTP协议进行,因此可以在WebSocket的HTTP请求头中添加请求头
const socket = new WebSocket('wss://example.com/socket');

socket.addEventListener('open', (event) => {
  socket.send('Authorization: Bearer ' + YOUR_TOKEN);
});
  1. 通过URL传参
const socket = new WebSocket('wss://example.com/socket?authorization=' + YOUR_TOKEN);

还有通过自定义子协议、某些支持扩展的WebSocket库两种方式,可自行了解。我说下我没有列举的原因

  1. 自定义子协议:携带信息过多时感觉不方便,比如:当我需要传租户id、应用id、用户token等等信息时不太方便
  2. 使用某些支持扩展的WebSocket库:具体哪些库需要花费时间查找、翻阅文档、测试,无疑会耗时耗力

所以我认为列举的两种方式能够满足一般WebSocket场景的使用。

参考链接

  1. #MDN WebSocket
  2. #the-websocket-interface
  3. #Support for custom headers for handshake
  4. #how-to-add-header-authorization-in-websocket