Configurator
实现的ServerEndpointConfig.Configurator.modifyHandshake()方法中执行此操作。我只是不知道该怎么做才能中止连接。有一个HandshakeResponse参数,允许向响应添加标头,但我找不到任何可以完成此任务的标头。那么,在握手期间如何中止websocket连接呢?这真的可能吗?
Configurator
实现的ServerEndpointConfig.Configurator.modifyHandshake()方法中执行此操作。我只是不知道该怎么做才能中止连接。有一个HandshakeResponse参数,允许向响应添加标头,但我找不到任何可以完成此任务的标头。你是对的,使用´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>());
}
我知道这是一个旧帖子,但我没有看到任何关于这个问题的改进。因此,也许有人可以讨论一下我的解决方案。
我尝试了leos版本,在Wildfly 8.0和Undertow 1.0上运行。
final ArrayList<String> finalEmpty = new ArrayList<String>();
response.getHeaders().put(HandshakeResponse.SEC_WEBSOCKET_ACCEPT,finalEmpty);
onOpen()
过程进行。onOpen()
,然后触发onError()
。在我的情况下,我记录了一些有关断开连接的客户端的信息,所以当出现错误时,我总是调用onClose()
。onOpen()
过程,不会触发onError()
。因此,您的服务器甚至不知道客户端已经断开连接。
请勿弄乱您的标头,并且不要让客户端做脏活。
相反,您应该将身份验证数据添加到配置中。/** some verification foo code...**/
config.getUserProperties().put("isValid",true);
在onOpen()
中,您需要检查isValid的值。如果它不是,则调用onClose(session,null);
,然后将会关闭session。
这并不是最好的解决方案,因为websocket身份验证很糟糕,每个浏览器有时都会表现不同。请参见:Websocket: Closing browser triggers onError() in chrome but onClose() event in Firefox
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 {
...
}
另一种技术:
当您从ServerEndpointConfig.Configurator#modifyHandshake
抛出RuntimeException时,连接不会建立。
这在Tomcat 8中有效。从Jetty示例中得到了这个想法,因此我想它在Jetty中也适用。
modifyHandshake()
之后,似乎服务器代码设置了Sec-WebSocket-Accept
头。像这样在modifyHandshake()
内部打印响应头:logger.info("response.getHeaders() => " + response.getHeaders());
我们得到这个:response.getHeaders() => {}
- Célio