Java JSR-356带Tomcat的WebSocket-如何限制单个IP地址内的连接?
我制作了一个JSR-356Java JSR-356带Tomcat的WebSocket-如何限制单个IP地址内的连接?,java,tomcat,servlets,websocket,jsr356,Java,Tomcat,Servlets,Websocket,Jsr356,我制作了一个JSR-356@ServerEndpoint,其中我想限制来自单个IP地址的活动连接,以防止简单的DDOS攻击 注意,我正在搜索Java解决方案(JSR-356、Tomcat或Servlet3.0规范) 我尝试过自定义端点配置器,但即使在握手请求对象中,我也无法访问IP地址 如何在没有iptables等外部软件的情况下从单个IP地址限制JSR-356连接计数?根据Tomcat developer@mark thomas client IP未通过JSR-356公开,因此不可能用纯JSR
@ServerEndpoint
,其中我想限制来自单个IP地址的活动连接,以防止简单的DDOS攻击
注意,我正在搜索Java解决方案(JSR-356、Tomcat或Servlet3.0规范)
我尝试过自定义端点配置器,但即使在握手请求
对象中,我也无法访问IP地址
如何在没有iptables等外部软件的情况下从单个IP地址限制JSR-356连接计数?根据Tomcat developer@mark thomas client IP未通过JSR-356公开,因此不可能用纯JSR-356 API-s实现此功能 您必须使用相当丑陋的黑客来绕过标准的限制 需要做的事情归结起来是:
ServletRequestListener
request.getSession()
,以确保其具有会话并将客户端IP存储为会话属性ServerEndpointConfig.Configurator
,它使用modifyHandshake
方法将客户端IP从HandshakeRequest#getHttpSession
提升,并将其作为用户属性附加到EndpointConfig
EndpointConfig
user properties获取客户端IP,将其存储在map或其他文件中,如果每个IP的会话数超过阈值,则触发清理逻辑@WebFilter
而不是ServletRequestListener
请注意,除非您的应用程序已经使用会话(例如用于身份验证),否则此选项可能会占用大量资源
将IP作为URL中的加密令牌传递
/mychat
ServletRequest#getRequestDispatcher
将请求转发到/mychat/TOKEN
@ServerEndpoint(“/mychat/{token}”)
@PathParam
中取出令牌并解密以获取客户端IP。如果每个IP的会话数超过阈值,则将其存储在map或其他文件中,并触发清理逻辑/mychat/2.3.4.5
,从而在未加密的情况下欺骗客户端IP
另见:
套接字对象隐藏在WsSession中,因此您可以使用反射来获取ip地址。该方法的执行时间约为1ms。这个解决方案不是完美的,而是有用的
public static InetSocketAddress getRemoteAddress(WsSession session) {
if(session == null){
return null;
}
Async async = session.getAsyncRemote();
InetSocketAddress addr = (InetSocketAddress) getFieldInstance(async,
"base#sos#socketWrapper#socket#sc#remoteAddress");
return addr;
}
private static Object getFieldInstance(Object obj, String fieldPath) {
String fields[] = fieldPath.split("#");
for(String field : fields) {
obj = getField(obj, obj.getClass(), field);
if(obj == null) {
return null;
}
}
return obj;
}
private static Object getField(Object obj, Class<?> clazz, String fieldName) {
for(;clazz != Object.class; clazz = clazz.getSuperclass()) {
try {
Field field;
field = clazz.getDeclaredField(fieldName);
field.setAccessible(true);
return field.get(obj);
} catch (Exception e) {
}
}
return null;
}
公共静态InetSocketAddress getRemoteAddress(WsSession会话){
if(会话==null){
返回null;
}
Async Async=session.getAsyncRemote();
InetSocketAddress addr=(InetSocketAddress)getFieldInstance(异步,
“基本#sos#SocketRapper#socket#sc#remoteAddress”);
返回地址;
}
私有静态对象getFieldInstance(对象obj,字符串fieldPath){
字符串字段[]=fieldPath.split(“#”);
用于(字符串字段:字段){
obj=getField(obj,obj.getClass(),field);
if(obj==null){
返回null;
}
}
返回obj;
}
私有静态对象getField(对象obj、类clazz、字符串fieldName){
对于(;clazz!=Object.class;clazz=clazz.getSuperclass()){
试一试{
场域;
field=clazz.getDeclaredField(字段名);
字段。setAccessible(true);
返回字段.get(obj);
}捕获(例外e){
}
}
返回null;
}
pom配置是
<dependency>
<groupId>javax.websocket</groupId>
<artifactId>javax.websocket-all</artifactId>
<version>1.1</version>
<type>pom</type>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-websocket</artifactId>
<version>8.0.26</version>
<scope>provided</scope>
</dependency>
javax.websocket
javax.websocket-all
1.1
聚甲醛
假如
org.apache.tomcat
TomcatWebSocket
8.0.26
假如
不要为此编写代码。使用防火墙。当您开始编写Java代码时,已经太晚了。@EJP作为问题状态-需要Java解决方案。我不是在要求最好的解决方案。假设我们希望产品在许多环境中都具有最大的简单性和可移植性。从单个主机建立40000个连接非常容易,但是自动断开连接会使这变得更加困难。Tomcat在websockets上使用max connections,因此,如果它被配置为处理~200k连接,我只想通过保持连接来防止非常容易的服务阻塞,这不需要像僵尸网络这样的大量资源。你假设有一个Java解决方案,没有证据。如果你在运输产品或有其他限制,你应该在问题中说明。大多数人不是。事实上,从一台主机上建立40000个连接并不容易。很多环境都不会超过几千个。你确定你的第二个建议有效吗?也许你能提供一个要点?在Jetty 9.3.6.v20151106中尝试转发升级请求时,至少出现了500个错误。直接连接到websocket很好。为了回答我自己的意见,第二个建议根本不起作用,并且在第一个选项中不可能使用过滤器,