Jul*_*oux 170 javascript header http websocket
看起来很容易使用任何支持此功能的HTTP头客户端向您的websocket客户端添加自定义HTTP标头,但我无法找到如何使用JSON API执行此操作.
任何人都知道如何实现它?
var ws = new WebSocket("ws://example.com/service");
Run Code Online (Sandbox Code Playgroud)
具体来说,我需要能够发送HTTP授权标头.
kan*_*aka 181
更新2x
简答:不,只能指定路径和协议字段.
更长的回答:
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"]);
Run Code Online (Sandbox Code Playgroud)
以上结果如下:
Sec-WebSocket-Protocol: protocol
Run Code Online (Sandbox Code Playgroud)
和
Sec-WebSocket-Protocol: protocol1, protocol2
Run Code Online (Sandbox Code Playgroud)
实现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")
Run Code Online (Sandbox Code Playgroud)
以上结果在以下标题中使用字符串"username:password"base64 encoded:
Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=
Run Code Online (Sandbox Code Playgroud)
我已经在Chrome 55和Firefox 50中测试了基本身份验证,并验证了基本身份验证信息确实与服务器协商(这可能在Safari中无效).
感谢Dmitry Frank的基本认可答案
kaq*_*qao 52
以下是对 2024 年以及之后很长一段时间内遇到这种情况的疲惫旅行者的情况的快速总结。
自从这个问题提出以来,12(!)年里什么都没有改变。JavaScript WebSocket API 已被所有浏览器供应商放弃(尽管实现偶尔会得到更新),并且新规范(WebSocket Stream和WebTransport)还远未实现。这一切意味着 WebSocket 仍然被广泛使用,尽管7 年前被称为“遗留”,但不存在损坏的 API 的替代品,但不存在损坏的 API 的替代品,并且问题中概述的问题与以前一样令人烦恼,甚至更多。
处理这种情况的选项(剧透,#5 或#6 就是你想要的):
在https://devcenter.heroku.com/articles/websocket-security中进行了描述。客户端应向专用端点发出经过身份验证的请求,该端点将生成并保留一个短期令牌,该令牌也将发送给客户端。然后,客户端在打开 WebSocket 时返回此令牌作为 URL 参数。服务器可以验证它并接受/拒绝协议升级。这就需要服务器专门针对WebSockets实现一个完全自定义的、有状态的认证机制,这在很多场景下是一座太过遥远的桥梁。
您在未进行身份验证的情况下打开 WebSocket,然后在执行其他操作之前通过 WebSocket 发送您的身份验证信息。从理论上讲,这听起来很合乎逻辑(并且是由浏览器供应商建议的),但如果只是粗略地思考一下,就会分崩离析。服务器旨在实现一种笨拙的、高度有状态的、完全自定义的身份验证机制,该机制与其他任何东西都不能很好地配合,除了必须与拒绝身份验证的客户端保持持久连接之外,还为拒绝身份验证的客户端敞开了大门。拒绝服务攻击,或者进入一个全新的兔子整体,强制执行严格的超时以防止恶意行为。
并不像听起来那么可怕,只要强制执行 SSL(wss://不是ws://),因为 WebSocket URL 很特殊,不会保存在浏览器历史记录或类似内容中。最重要的是,访问令牌通常是短暂的,因此也减轻了危险。但。无论如何,服务器很可能会在某个时刻记录该 URL。即使您的服务器应用程序不这样做,框架或(云)主机也可能会这样做。此外,如果您必须传递 ID 令牌(就像 Firebase 习惯做的那样),那么随着 ID 令牌变得巨大,您可能会遇到各种 URL 长度限制。
不。WebSocket 不受同源策略的约束(因为显然 WebSocket 的每一个小细节都非常糟糕),并且允许 cookie 会让您很容易受到 CSRF 攻击。使用 CSRF 令牌修复此问题的描述如下,但它比采用此列表中的任何其他方法更困难,因此根本不值得考虑。
Sec-WebSocket-Protocol由于浏览器允许您控制的唯一标头是Sec-WebSocket-Protocol,因此您可以滥用它来模拟任何其他标头。有趣(或者更滑稽的是),这就是Kubernetes 正在做的事情。简而言之,您可以将身份验证所需的任何内容附加为内部额外支持的子协议Sec-WebSocket-Protocol:
var ws = new WebSocket("ws://example.com/path", ["realProtocol", "yourAccessTokenOrSimilar"]);
Run Code Online (Sandbox Code Playgroud)
然后,在服务器上,您添加某种中间件,在将请求进一步传递到系统之前将其转换回更清晰的形式。是的,很糟糕,但迄今为止最好的解决方案。URL 中没有令牌,没有为小型中间件保存的自定义身份验证,服务器上不需要额外的状态。不要忘记包含真正的子协议,因为各种工具将拒绝没有子协议的连接。
对于很多情况,SSE 可能是一个不错的替代品。浏览器EventSourceAPI 严重损坏WebSocket(除了 GET 请求之外它无法发送任何内容,尽管是常规 HTTP,但也无法发送标头),但是!它可以很容易地被替换为fetch一个更健全的 API。这种方法可以很好地替代 WebSocket,例如在 GraphQL 订阅中,或者在不强制要求全双工的任何地方。这可能涵盖了大多数场景。RSocket理论上也可以是一个选项,但看看它是如何通过浏览器中的 WebSocket 实现的,我认为它实际上并不能解决任何问题,但我没有对其进行足够深入的研究,无法绝对肯定地说。
Dmi*_*ank 32
可以使用以下方法解决HTTP授权标头问题:
var ws = new WebSocket("ws://username:password@example.com/service");
Run Code Online (Sandbox Code Playgroud)
然后,将使用提供的username和设置适当的基本授权HTTP标头password.如果您需要基本授权,那么您已经完成了所有设置.
我想使用Bearer,然后我采用了以下技巧:我按如下方式连接到服务器:
var ws = new WebSocket("ws://my_token@example.com/service");
Run Code Online (Sandbox Code Playgroud)
当我的服务器端代码收到带有非空用户名和空密码的Basic Authorization标头时,它会将用户名解释为令牌.
Tim*_*Tim 25
更多的替代解决方案,但所有现代浏览器都会发送域cookie以及连接,因此使用:
var authToken = 'R3YKZFKBVi';
document.cookie = 'X-Authorization=' + authToken + '; path=/';
var ws = new WebSocket(
'wss://localhost:9000/wss/'
);
Run Code Online (Sandbox Code Playgroud)
最后得到请求连接标头:
Cookie: X-Authorization=R3YKZFKBVi
Run Code Online (Sandbox Code Playgroud)
Pet*_*ndi 24
对于那些在 2021 年仍在苦苦挣扎的人来说,Node JS 全局 Web 套接字类options在构造函数中有一个附加字段。如果你去查看WebSockets类的实现,你会发现这个变量声明。您可以看到它接受三个 params 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;
};
Run Code Online (Sandbox Code Playgroud)
如果您使用的是 Node Js 库,例如react、react-native。这是一个如何做到这一点的示例。
const ws = new WebSocket(WEB_SOCKETS_URL, null, {
headers: {
['Set-Cookie']: cookie,
},
});
Run Code Online (Sandbox Code Playgroud)
请注意我已通过 null 的协议。如果您使用的是 jwt,则可以使用Bearer+传递 Authorization 标头token
免责声明,并非所有浏览器都支持此功能,从 MDN Web 文档中您可以看到仅记录了两个参数。请参阅 https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/WebSocket#syntax
Gab*_*oli 18
您无法添加标题,但是,如果您只需要在连接时将值传递给服务器,则可以在URL上指定查询字符串部分:
var ws = new WebSocket("ws://example.com/service?key1=value1&key2=value2");
Run Code Online (Sandbox Code Playgroud)
该URL有效,但 - 当然 - 您需要修改服务器代码以解析它.
Sae*_*fam 12
如果要使用JavaScript WebSockets API建立WebSockets连接,则无法发送自定义标头.您可以使用Subprotocols第二个WebSocket类构造函数来使用标头:
var ws = new WebSocket("ws://example.com/service", "soap");
Run Code Online (Sandbox Code Playgroud)
然后您可以使用Sec-WebSocket-Protocol服务器上的密钥获取Subprotocols标头.
还有一个限制,您的Subprotocols标头值不能包含逗号(,)!
小智 9
无法发送授权标头。
附加令牌查询参数是一种选择。但是,在某些情况下,可能不希望以纯文本形式将主登录令牌作为查询参数发送,因为它比使用标头更不透明,并且最终会被记录在任何地方。如果这给您带来了安全方面的担忧,则替代方法是仅将辅助JWT令牌用于Web套接字内容。
创建一个用于生成此JWT的REST端点,当然,只有使用您的主要登录令牌(通过标头传输)进行身份验证的用户才能访问该端点。Web套接字JWT的配置可以与登录令牌不同,例如,超时时间更短,因此,将其作为升级请求的查询参数进行发送时更加安全。
为您在上注册SockJS eventbusHandler的同一路由创建一个单独的JwtAuthHandler。确保首先注册了您的身份验证处理程序,以便您可以针对数据库检查Web套接字令牌(JWT应该以某种方式链接到后端的用户)。
感谢卡纳卡的回答,完全像这样破解了它。
客户:
var ws = new WebSocket(
'ws://localhost:8080/connect/' + this.state.room.id,
store('token') || cookie('token')
);
Run Code Online (Sandbox Code Playgroud)
服务器(在本例中使用 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...
Run Code Online (Sandbox Code Playgroud)
在我的情况下(Azure 时序见解 wss://)
使用 ReconnectingWebsocket 包装器并能够通过简单的解决方案实现添加标头:
socket.onopen = function(e) {
socket.send(payload);
};
Run Code Online (Sandbox Code Playgroud)
本例中的有效负载是:
{
"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
}}}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
147615 次 |
| 最近记录: |