JSR-356:如何在握手期间中止 Websocket 连接?

问题描述 投票:0回答:5

我需要能够在握手期间中止 Websocket 连接,以防 HTTP 请求不满足某些条件。据我了解,正确的位置是在我自己的 Configurator 实现的

ServerEndpointConfig.Configurator.modifyHandshake()
方法中。我只是不知道该怎么做才能中止连接。有一个 HandshakeResponse 参数允许向响应添加标头,但我找不到任何可以完成这项工作的标头。

那么如何在握手期间中止 Websocket 连接呢?这可能吗?

java jakarta-ee websocket java-7 java-ee-7
5个回答
4
投票

你是对的,使用“modifyHandShake()”来更新响应标头,您需要准确地删除或设置标头的值

Sec-WebSocket-Accept
,请从规范

检查这一点

|Sec-WebSocket-Accept|头域表示是否 服务器愿意接受连接。 如果存在,则此 标头字段必须包含客户端发送的随机数的哈希值 |Sec-WebSocket-Key|以及预定义的 GUID。 任何其他值 不得被解释为接受连接 服务器。

    HTTP/1.1 101 Switching Protocols
    Upgrade: websocket
    Connection: Upgrade
    Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=

这些字段由 WebSocket 客户端检查脚本页面。 如果 |Sec-WebSocket-Accept|值与预期不符 值,如果标头字段缺失,或者 HTTP 状态代码为 不是101,连接将不会建立,并且WebSocket帧 不会被发送。

你的代码看起来像这样:

 @Override
public void modifyHandshake(ServerEndpointConfig sec,
    HandshakeRequest request, HandshakeResponse response) {
    super.modifyHandshake(sec, request, response);
    response.getHeaders().put(HandshakeResponse.SEC_WEBSOCKET_ACCEPT, new ArrayList<String>());
}

浏览器会将此解释为服务器未接受连接。例如在 chrome 中我收到消息

Websocket 握手时出错


3
投票

我知道 - 旧线程,但我没有看到任何改进。所以也许有人可以讨论我对这个问题的解决方案。

我尝试了 leos 版本,运行 Wildfly 8.0 、Undertow 1.0

final  ArrayList<String> finalEmpty = new ArrayList<String>();
response.getHeaders().put(HandshakeResponse.SEC_WEBSOCKET_ACCEPT,finalEmpty);

这会导致一些有趣的行为:

即使您的浏览器应该关闭连接,它们也会经历

onOpen()
过程。

  • Chrome:将触发
    onOpen()
    ,然后触发
    onError()
    。在我的 案例中,我对断开连接的客户端做了一些记录,所以我总是打电话
    onClose()
    出现错误时。
  • IE:将像 Chrome 一样运行
  • Firefox:Firefox 将只运行 onOpen() 过程。并且不会触发onError()。所以你的服务器甚至不知道客户端已断开连接。

不要弄乱你的标题,也不要让客户做这些工作。

相反,您应该将身份验证数据添加到配置中。

/** some verification foo code...**/

config.getUserProperties().put("isValid",true);

然后在

onOpen()
中检查 isValid 的值。如果不是,请致电
onClose(session,null);
。并且会议将关闭。

这不是最好的解决方案,但那是因为 websocket 身份验证很糟糕,而且每个浏览器有时表现不同。请参阅:Websocket:关闭浏览器在 chrome 中会触发 onError(),但在 Firefox 中会触发 onClose() 事件


2
投票

最简单的方法是在 web.xml 中设置简单的 Web 过滤器,该过滤器将在握手之前执行。

喜欢:

public class MyHandshakeFilter implements Filter {

@Override
public void init(FilterConfig config) throws ServletException {
    // do nothing
}

/**
 * Passes request to chain when request port is equal to specified in the allowedPort property
 * and returns HttpServletResponse.SC_NOT_FOUND in other case.
 */
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException,
        ServletException {

    if (<your validation here based on http request>) {
        // request conditions are met. continue handshake...
        chain.doFilter(request, response);
    } else {
        // request conditions are NOT met. reject handshake...
        ((HttpServletResponse) response).setStatus(HttpServletResponse.SC_FORBIDDEN);
    }
}
}

在 web.xml 中:

<filter>
    <filter-name>yourHandshakeFilter</filter-name>
    <filter-class>com....MyHandshakeFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>yourHandshakeFilter</filter-name>
    <url-pattern>/yourEndpointUrl</url-pattern>
</filter-mapping>

你的 ServerEndpoint 应该是这样的:

@ServerEndpoint("/yourEndpointUrl")
public class MyServerEndpoint {
...
}

2
投票

另一种技巧:

当您从

ServerEndpointConfig.Configurator#modifyHandshake
抛出 RuntimeException 时,则未建立连接。

这适用于 Tomcat 8。从 Jetty 示例得到了这个想法,所以我想它也适用于 Jetty。


0
投票

我在仅为授权用户实现 websocket 时遇到了同样的问题。不幸的是,Tomcat 不会修改标头,而是通过modifyHandshake 添加它们。但在此之前,它会检查 checkOrigin,如果 checkOrigin 返回 false,则抛出正确的 403 代码。 header不合适,但是我没有看到Websocket规范中描述了它的使用。最后,我是这样做的,它应该适用于Tomcat,我没有检查其他服务器。

 public class WebsocketAuthConfigurator extends ServerEndpointConfig.Configurator {
      @Override
      public boolean checkOrigin(String originHeaderValue) {
        return "testAuthKey".equals(originHeaderValue);
      } 
 }

对于客户

public class WebsocketTestConfigurator extends ClientEndpointConfig.Configurator {
  @Override
  public void beforeRequest(Map<String, List<String>> headers) {
    headers.put("Origin", List.of("testAuthKey"));
  }
}
© www.soinside.com 2019 - 2024. All rights reserved.