看起来使用支持自定义HTTP头的任何HTTP头客户端都很容易向您的WebSocket客户端添加自定义HTTP头,但我找不到如何在Web平台的 WebSocket
API中执行此操作。
有人知道如何实现吗?
var ws = new WebSocket("ws://example.com/service");
具体来说,我需要能够发送一个HTTP授权头。
看起来使用支持自定义HTTP头的任何HTTP头客户端都很容易向您的WebSocket客户端添加自定义HTTP头,但我找不到如何在Web平台的 WebSocket
API中执行此操作。
有人知道如何实现吗?
var ws = new WebSocket("ws://example.com/service");
具体来说,我需要能够发送一个HTTP授权头。
更新2次
简短回答:不行,只能指定路径和协议字段。
更详细的回答:
在JavaScript的WebSockets API中没有为客户端/浏览器发送附加标头指定的方法。WebSocket构造函数可以指定HTTP路径(“GET /xyz”)和协议标头(“Sec-WebSocket-Protocol”)。
Sec-WebSocket-Protocol标头(有时扩展用于websocket特定的身份验证)是从WebSocket构造函数的可选第二个参数生成的:
var ws = new WebSocket("ws://example.com/path", "protocol");
var ws = new WebSocket("ws://example.com/path", ["protocol1", "protocol2"]);
以上代码的结果是以下标头:
Sec-WebSocket-Protocol: protocol
并且
Sec-WebSocket-Protocol: protocol1, protocol2
实现WebSocket身份验证和授权的常见模式是通过实施票证系统。其中,托管WebSocket客户端的页面会向服务器请求一张票,并在WebSocket连接设置期间将此票传递,在URL /查询字符串中,在协议字段中或作为连接建立后第一条消息的必需部分中传递此票。然后,如果票证有效(存在、尚未被使用、客户端IP编码在票证中匹配、票证中的时间戳是最近的等),则服务器仅允许继续进行连接。以下是WebSocket安全信息概要:https://devcenter.heroku.com/articles/websocket-security。基本身份验证曾经是一种选项,但已被弃用,即使指定了该标头,现代浏览器也不发送该标头。基本身份验证信息(已弃用-不再可用):注意:以下信息在任何现代浏览器中均不再准确。
Authorization标头是由WebSocket URI的用户名和密码(或仅用户名)字段生成的:
var ws = new WebSocket("ws://username:password@example.com")
上述代码将生成以下带有字符串“username:password”进行base64编码的标头:
Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=
我已经在Chrome 55和Firefox 50中测试了基本身份验证,并验证了基本身份验证信息确实与服务器协商(这在Safari上可能无法工作)。
感谢Dmitry Frank的基本身份验证答案
这是一个备选方案,但现代浏览器会在连接时发送域Cookie,因此可以使用:
var authToken = 'R3YKZFKBVi';
document.cookie = 'X-Authorization=' + authToken + '; path=/';
var ws = new WebSocket(
'wss://localhost:9000/wss/'
);
最终获得请求连接头:
Cookie: X-Authorization=R3YKZFKBVi
https://example.com/login
并使响应在/wss
上设置一个cookie,然后new WebSocket("wss://example.com/wss")
将使用相关cookie开始握手请求。请注意,开发工具可能不会显示cookie,但它仍然应该被发送。 - Codererws = new WebSocket("wss://example.com/wss?csrftoken=<token>")
。 - Alex Buchatski无法发送Authorization头。
可以附加令牌查询参数。然而,在某些情况下,将主要的登录令牌作为明文查询参数发送可能是不想要的,因为这比使用头更不透明,并最终被记录在不确定的位置。如果这对您造成安全问题,另一种选择是仅用于Web Socket的第二个JWT令牌。
创建一个REST端点来生成此JWT,当然只能由使用主要登录令牌(通过头传输)进行身份验证的用户访问。 Web Socket JWT可以与您的登录令牌配置不同,例如具有较短的超时时间,因此作为升级请求的查询参数发送更安全。
为您在其上注册SockJS eventbusHandler的相同路由创建单独的JwtAuthHandler。确保您的auth处理程序首先注册,以便您可以检查Web Socket令牌是否与后台中的用户相关联(JWT应该以某种方式与用户关联)。
HTTP授权头问题可以通过以下方式解决:
var ws = new WebSocket("ws://username:password@example.com/service");
然后,将使用提供的 username
和 password
设置适当的基本身份验证 HTTP 标头。如果需要基本身份验证,则已完成设置。
我想使用 Bearer
,因此我采用以下小技巧:我按如下方式连接到服务器:
var ws = new WebSocket("ws://my_token@example.com/service");
当我的服务器端代码接收到带有非空用户名和空密码的基本授权头时,将把用户名解释为令牌。
wss://user:password@myhost.com/ws
(例如)并且在服务器端没有收到Authorization
头信息 (使用Chrome版本60)。 - user9645http://username:password@example.com
已经过时。可能这也是它不能与WebSockets一起使用的原因。https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication。 - Dachstein使用JavaScript WebSockets API建立WebSockets连接时,无法发送自定义头信息。
您可以通过使用第二个WebSocket类构造函数来使用Subprotocols
头信息:
var ws = new WebSocket("ws://example.com/service", "soap");
然后您可以在服务器上使用Sec-WebSocket-Protocol
键获取子协议标头。
还有一个限制,您的子协议标头值不能包含逗号(,
)!
你无法添加标题,但如果你只需要在连接时将值传递给服务器,可以在URL上指定查询字符串:
var ws = new WebSocket("ws://example.com/service?key1=value1&key2=value2");
那个URL是有效的,但是 - 当然 - 你需要修改你的服务器代码来解析它。
对于仍在2021年苦苦挣扎的人来说,Node JS全局网络套接字类在构造函数中有一个额外的options
字段。如果您进入WebSockets类的实现,您将找到此变量声明。您可以看到它接受三个参数url
,这是必需的,protocols
(可选),它可以是字符串、字符串数组或null。然后是第三个参数,即options
。我们感兴趣的是一个对象,仍然是可选的。详见...
declare var WebSocket: {
prototype: WebSocket;
new (
uri: string,
protocols?: string | string[] | null,
options?: {
headers: { [headerName: string]: string };
[optionName: string]: any;
} | null,
): WebSocket;
readonly CLOSED: number;
readonly CLOSING: number;
readonly CONNECTING: number;
readonly OPEN: number;
};
如果你正在使用Node Js库,如React、React-Native,这里是一个示例,展示如何完成相应操作。
const ws = new WebSocket(WEB_SOCKETS_URL, null, {
headers: {
['Set-Cookie']: cookie,
},
});
关于我传递了空值的协议,请注意。如果您正在使用jwt,可以通过 Bearer
+ token
传递授权标头。
免责声明,这可能不受所有浏览器的支持。从MDN网站文档中可以看到只有两个参数是记录在案的。 请参见https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/WebSocket#syntax
您在未经身份验证的情况下打开WebSocket,然后在执行其他任何操作之前通过WebSocket发送您的身份验证信息。从理论上讲,这听起来是合乎逻辑的(并且是浏览器供应商建议的),但只要稍加思考就会发现其中的问题。服务器需要实现一个笨拙、高度有状态且完全定制的身份验证机制,这与其他任何东西都不兼容,而且还需要与拒绝进行身份验证的客户端保持持久连接,这为拒绝服务攻击留下了一个大门,或者需要进行严格的超时限制以防止恶意行为。
3. 通过URL参数发送认证信息(例如访问令牌)Sec-WebSocket-Protocol
中走私访问令牌Sec-WebSocket-Protocol
,您可以滥用它来模拟任何其他标题。有趣的是(或者可以说是滑稽的),这就是Kubernetes正在做的事情。简而言之,您将所需的身份验证附加为Sec-WebSocket-Protocol
中的额外支持的子协议:var ws = new WebSocket("ws://example.com/path", ["realProtocol", "yourAccessTokenOrSimilar"]);
var ws = new WebSocket(
'ws://localhost:8080/connect/' + this.state.room.id,
store('token') || cookie('token')
);
服务器 (在这个例子中使用Koa2,但无论在哪里都应该类似):
var url = ctx.websocket.upgradeReq.url; // can use to get url/query params
var authToken = ctx.websocket.upgradeReq.headers['sec-websocket-protocol'];
// Can then decode the auth token and do any session/user stuff...
在我的情况下(Azure 时间序列洞察 wss://)
使用 ReconnectingWebsocket 包装器,通过简单的解决方案能够实现添加标头:
socket.onopen = function(e) {
socket.send(payload);
};
在这种情况下,payload是指:
{
"headers": {
"Authorization": "Bearer TOKEN",
"x-ms-client-request-id": "CLIENT_ID"
},
"content": {
"searchSpan": {
"from": "UTCDATETIME",
"to": "UTCDATETIME"
},
"top": {
"sort": [
{
"input": {"builtInProperty": "$ts"},
"order": "Asc"
}],
"count": 1000
}}}
connect
请求。我在后端使用 Django channels,并将其设计为在connect
事件上接受连接。然后,在receive
事件中设置一个“is_auth”标志(如果它看到有效的身份验证消息)。如果未设置 is_auth 标志并且不是身份验证消息,则关闭连接。 - Himal