自定义握手
在上篇的文章测试中我们使用参数123321作为用户的userId来进行通信;
实际业务中,一般使用登录用户的userId、userName、token等信息来自定义握手规则,并实现客户端和服务端的点对点信息推送或者广播推送;
@ServerEndpoint注解的源码
WebSocketServer 中我们应用了@ServerEndpoint注解,来看看源码
package javax.websocket.server;
@java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
@java.lang.annotation.Target({
java.lang.annotation.ElementType.TYPE})
public @interface ServerEndpoint {
java.lang.String value();
java.lang.String[] subprotocols() default {
};
java.lang.Class<? extends javax.websocket.Decoder>[] decoders() default {
};
java.lang.Class<? extends javax.websocket.Encoder>[] encoders() default {
};
java.lang.Class<? extends javax.websocket.server.ServerEndpointConfig.Configurator> configurator() default javax.websocket.server.ServerEndpointConfig.Configurator.class;
}
首先理解这个类的作用(下图):它可以拦截握手,我们看到最后的一个方法(上面代码),它要求返回一个ServerEndpointConfig.Configurator的子类;
我们写一个WebsocketEndpointConfig类去继承它,然后重写它的方法modifyHandshake,这个方法的作用就是修改握手,也就是自定义握手的规则。
参数:
sec- 握手中涉及的配置对象
request- 开放握手请求。
response- 提议的开放握手响应
当我们覆盖modifyHandshake方法时可以看到三个参数,我们查看HandshakeRequest的源码:
public interface HandshakeRequest {
static final String SEC_WEBSOCKET_KEY = "Sec-WebSocket-Key";
static final String SEC_WEBSOCKET_PROTOCOL = "Sec-WebSocket-Protocol";
static final String SEC_WEBSOCKET_VERSION = "Sec-WebSocket-Version";
static final String SEC_WEBSOCKET_EXTENSIONS= "Sec-WebSocket-Extensions";
Map<String,List<String>> getHeaders();
Principal getUserPrincipal();
URI getRequestURI();
boolean isUserInRole(String role);
/**
* Get the HTTP Session object associated with this request. Object is used
* to avoid a direct dependency on the Servlet API.
* @return The javax.servlet.http.HttpSession object associated with this
* request, if any.
*/
Object getHttpSession();
Map<String, List<String>> getParameterMap();
String getQueryString();
}
源码中的方法里有两个方法是实现握手的关键:
//获取请求中的所有封住的header信息
Map<String,List<String>> getHeaders();
//获取所有请求的参数
Map<String, List<String>> getParameterMap();
上面我们说了,一般我们使用一般使用登录用户的userId、userName、token等信息来自定义握手规则,而这些信息都可以从这两个方法中获取到;
再来看下ServerEndpointConfig,发现这个接口继承了EndpointConfig的接口
public interface ServerEndpointConfig extends EndpointConfig
好,我们看一下EndpointConfig的源码:
public interface EndpointConfig {
List<Class<? extends Encoder>> getEncoders();
List<Class<? extends Decoder>> getDecoders();
Map<String,Object> getUserProperties();
}
我们发现了这样的一个方法定义:
Map<String,Object> getUserProperties();
可以看到,它是一个map,从方法名也可以理解到,它是用户的一些属性的存储,那既然它提供了get方法,那么也就意味着我们可以拿到这个map,并且对这里面的值进行操作;
操作之前的数据结构如下:
我们可以看到这个userProperties中在调用的时候存储了websocket的事件属性,现在我们要做的就是将拿到的userId、userName、token等信息存储到userProperties中,在事件的处理方法中使用;
操作之后的数据结构如下:
有个疑问,perEndpointConfig和perSessionUserProperties这两个参数是怎么来的,为什么传入的token会出现在perSessionUserProperties中?
首先,我们要知道ServerEndpointConfig调用的getUserProperties()是其父类EndpointConfig的方法,EndpointConfig有多个实现类,其中WsPerSessionServerEndpointConfig对其做了解释;
WsPerSessionServerEndpointConfig源码:
class WsPerSessionServerEndpointConfig implements ServerEndpointConfig {
private final ServerEndpointConfig perEndpointConfig;
private final Map<String,Object> perSessionUserProperties =
new ConcurrentHashMap<>();
WsPerSessionServerEndpointConfig(ServerEndpointConfig perEndpointConfig) {
this.perEndpointConfig = perEndpointConfig;
perSessionUserProperties.putAll(perEndpointConfig.getUserProperties());
}
@Override
public List<Class<? extends Encoder>> getEncoders() {
return perEndpointConfig.getEncoders();
}
@Override
public List<Class<? extends Decoder>> getDecoders() {
return perEndpointConfig.getDecoders();
}
@Override
public Map<String,Object> getUserProperties() {
return perSessionUserProperties;
}
@Override
public Class<