`

jetty防止Dos攻击的filter实现分析

 
阅读更多

jetty的org.eclipse.jetty.servlets.DoSFilter类是用来实现Dos攻击预防的filter,里面涉及到一些变量,先了解下变量的含义:

 

    protected long _delayMs;超过最大处理请求数当前请求的等待时间,-1立即拒绝,0,无限等待,正数表达等待的毫秒数

    protected long _throttleMs;异步等待获取信号量的时间

    protected long _maxWaitMs;阻塞等待获取信号量的时间

    protected long _maxRequestMs;请求处理最大时间限制

    protected long _maxIdleTrackerMs;跟踪连接是否断开的最大等待时间

    protected int _throttledRequests;允许在等待队列中等待获取信号量的请求数

    protected int _maxRequestsPerSec; 每秒允许处理最多的请求数,超过将延迟,异步等待。

 

    protected boolean _insertHeaders; 是否往response写入dosfilter信息,默认true

    protected boolean _trackSessions;是否根据session来检测dos攻击,默认true

    protected boolean _remotePort;是否根据ip+port来检测dos攻击,默认false

protected String _whitelistStr;  白名单 ip白名单列表,这些通过都通过配置servlet的init-p

 

aram可以来重新设置。

 

首先看看init方法的初始化设置:

 

 public void init(FilterConfig filterConfig)
    {
        _context = filterConfig.getServletContext();

        _queue = new Queue[getMaxPriority() + 1];
        _listener = new ContinuationListener[getMaxPriority() + 1];
        for (int p = 0; p < _queue.length; p++)
        {
            _queue[p] = new ConcurrentLinkedQueue<Continuation>();

            final int priority=p;
            _listener[p] = new ContinuationListener()
            {
                public void onComplete(Continuation continuation)
                {
                }

                public void onTimeout(Continuation continuation)
                {
                    _queue[priority].remove(continuation);
                }
            };
        }

        _rateTrackers.clear();

        int baseRateLimit = __DEFAULT_MAX_REQUESTS_PER_SEC;
        if (filterConfig.getInitParameter(MAX_REQUESTS_PER_S_INIT_PARAM) != null)
            baseRateLimit = Integer.parseInt(filterConfig.getInitParameter(MAX_REQUESTS_PER_S_INIT_PARAM));
        _maxRequestsPerSec = baseRateLimit;

        long delay = __DEFAULT_DELAY_MS;
        if (filterConfig.getInitParameter(DELAY_MS_INIT_PARAM) != null)
            delay = Integer.parseInt(filterConfig.getInitParameter(DELAY_MS_INIT_PARAM));
        _delayMs = delay;

        int throttledRequests = __DEFAULT_THROTTLE;
        if (filterConfig.getInitParameter(THROTTLED_REQUESTS_INIT_PARAM) != null)
            throttledRequests = Integer.parseInt(filterConfig.getInitParameter(THROTTLED_REQUESTS_INIT_PARAM));
        _passes = new Semaphore(throttledRequests,true);
        _throttledRequests = throttledRequests;

        long wait = __DEFAULT_WAIT_MS;
        if (filterConfig.getInitParameter(MAX_WAIT_INIT_PARAM) != null)
            wait = Integer.parseInt(filterConfig.getInitParameter(MAX_WAIT_INIT_PARAM));
        _maxWaitMs = wait;

        long suspend = __DEFAULT_THROTTLE_MS;
        if (filterConfig.getInitParameter(THROTTLE_MS_INIT_PARAM) != null)
            suspend = Integer.parseInt(filterConfig.getInitParameter(THROTTLE_MS_INIT_PARAM));
        _throttleMs = suspend;

        long maxRequestMs = __DEFAULT_MAX_REQUEST_MS_INIT_PARAM;
        if (filterConfig.getInitParameter(MAX_REQUEST_MS_INIT_PARAM) != null )
            maxRequestMs = Long.parseLong(filterConfig.getInitParameter(MAX_REQUEST_MS_INIT_PARAM));
        _maxRequestMs = maxRequestMs;

        long maxIdleTrackerMs = __DEFAULT_MAX_IDLE_TRACKER_MS_INIT_PARAM;
        if (filterConfig.getInitParameter(MAX_IDLE_TRACKER_MS_INIT_PARAM) != null )
            maxIdleTrackerMs = Long.parseLong(filterConfig.getInitParameter(MAX_IDLE_TRACKER_MS_INIT_PARAM));
        _maxIdleTrackerMs = maxIdleTrackerMs;

        _whitelistStr = "";
        if (filterConfig.getInitParameter(IP_WHITELIST_INIT_PARAM) !=null )
            _whitelistStr = filterConfig.getInitParameter(IP_WHITELIST_INIT_PARAM);
        initWhitelist();

        String tmp = filterConfig.getInitParameter(INSERT_HEADERS_INIT_PARAM);
        _insertHeaders = tmp==null || Boolean.parseBoolean(tmp);

        tmp = filterConfig.getInitParameter(TRACK_SESSIONS_INIT_PARAM);
        _trackSessions = tmp==null || Boolean.parseBoolean(tmp);

        tmp = filterConfig.getInitParameter(REMOTE_PORT_INIT_PARAM);
        _remotePort = tmp!=null&& Boolean.parseBoolean(tmp);

        _requestTimeoutQ.setNow();
        _requestTimeoutQ.setDuration(_maxRequestMs);

        _trackerTimeoutQ.setNow();
        _trackerTimeoutQ.setDuration(_maxIdleTrackerMs);

        _running=true;
        _timerThread = (new Thread()
        {
            public void run()
            {
                try
                {
                    while (_running)
                    {
                        long now;
                        synchronized (_requestTimeoutQ)
                        {
                            now = _requestTimeoutQ.setNow();
                            _requestTimeoutQ.tick();
                        }
                        synchronized (_trackerTimeoutQ)
                        {
                            _trackerTimeoutQ.setNow(now);
                            _trackerTimeoutQ.tick();
                        }
                        try
                        {
                            Thread.sleep(100);
                        }
                        catch (InterruptedException e)
                        {
                            Log.ignore(e);
                        }
                    }
                }
                finally
                {
                    Log.info("DoSFilter timer exited");
                }
            }
        });
        _timerThread.start();

        if (_context!=null && Boolean.parseBoolean(filterConfig.getInitParameter(MANAGED_ATTR_INIT_PARAM)))
            _context.setAttribute(filterConfig.getFilterName(),this);
    }

 _queue是用来保存当前当前通过header,session,ip等检测类型的请求队列,然后就是一堆参数的初始化设置,最后

又有两个queue _requestTimeoutQ是来保存每个请求的处理时间超时检测的队列;_trackerTimeoutQ是来检测请求对应连接是否已经关闭超时的检测队列;最后启动一个_timerThread线程来进行这两个队列的超时检测。

 

然后再看下dofilter的处理

  public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterchain) throws IOException, ServletException
    {
        final HttpServletRequest srequest = (HttpServletRequest)request;
        final HttpServletResponse sresponse = (HttpServletResponse)response;

        final long now=_requestTimeoutQ.getNow();

        // Look for the rate tracker for this request
        RateTracker tracker = (RateTracker)request.getAttribute(__TRACKER); 

        if (tracker==null) //如果request没有进行过dosfilter的处理
        {
            // This is the first time we have seen this request.

            // get a rate tracker associated with this request, and record one hit
            tracker = getRateTracker(request); //根据request生成dos跟踪的对象,看下面方法

            // Calculate the rate and check it is over the allowed limit
            final boolean overRateLimit = tracker.isRateExceeded(now);//判断是否已经超过每秒最大处理请求数

            // pass it through if  we are not currently over the rate limit
            if (!overRateLimit)//如果没有超过,则正常处理
            {
                doFilterChain(filterchain,srequest,sresponse);
                return;
            }

            // We are over the limit.
            Log.warn("DOS ALERT: ip="+srequest.getRemoteAddr()+",session="+srequest.getRequestedSessionId()+",user="+srequest.getUserPrincipal());

            // So either reject it, delay it or throttle it
            switch((int)_delayMs) //根据当前配置的延时时间
            {
                case -1: //如果为-1,则直接拒绝
                {
                    // Reject this request
                    if (_insertHeaders) //是否把dosfilter处理插入response的header
                        ((HttpServletResponse)response).addHeader("DoSFilter","unavailable");
                    ((HttpServletResponse)response).sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
                    return;
                }
                case 0: //如果为0,则当前线程继续等待处理,
                {
                    // fall through to throttle code
                    request.setAttribute(__TRACKER,tracker);
                    break;
                }
                default://其他字段,则设置request的timeout时间为_delayMs,并且挂起当前线程,返回
                {
                    // insert a delay before throttling the request
                    if (_insertHeaders)
                        ((HttpServletResponse)response).addHeader("DoSFilter","delayed");
                    Continuation continuation = ContinuationSupport.getContinuation(request);
                    request.setAttribute(__TRACKER,tracker);
                    if (_delayMs > 0)
                        continuation.setTimeout(_delayMs);
                    continuation.suspend();
                    return;
                }
            }
        }

//_delayMs为0是,当前请求继续等待
        // Throttle the request
        boolean accepted = false;
        try
        {
            // check if we can afford to accept another request at this time
            accepted = _passes.tryAcquire(_maxWaitMs,TimeUnit.MILLISECONDS);//判断当前处理请求队列是否已经有处理完的请求,处理完的会释放信号量,则当前请求线程可以获取信号量

            if (!accepted) //如果不能获取,则把当前请求设置为异步等待,异步等待的时间为_throttleMs,
            {
                // we were not accepted, so either we suspend to wait,or if we were woken up we insist or we fail
                final Continuation continuation = ContinuationSupport.getContinuation(request);

                Boolean throttled = (Boolean)request.getAttribute(__THROTTLED);
                if (throttled!=Boolean.TRUE && _throttleMs>0)
                {
                    int priority = getPriority(request,tracker);
                    request.setAttribute(__THROTTLED,Boolean.TRUE);
                    if (_insertHeaders)
                        ((HttpServletResponse)response).addHeader("DoSFilter","throttled");
                    if (_throttleMs > 0)
                        continuation.setTimeout(_throttleMs);
                    continuation.suspend();

                    continuation.addContinuationListener(_listener[priority]);
                    _queue[priority].add(continuation);
                    return;
                }
                // else were we resumed?
                else if  (request.getAttribute("javax.servlet.resumed")==Boolean.TRUE) 
                {//如果线程中心被唤醒,则可以获取到信号量,
                    // we were resumed and somebody stole our pass, so we wait for the next one.
                    _passes.acquire();
                    accepted = true;
                }
            }

            // if we were accepted (either immediately or after throttle)
            if (accepted) //获取到了,继续执行,
                // call the chain
                doFilterChain(filterchain,srequest,sresponse);
            else
            {
                // fail the request
                if (_insertHeaders)
                    ((HttpServletResponse)response).addHeader("DoSFilter","unavailable");
                ((HttpServletResponse)response).sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
            }
        }
        catch (InterruptedException e)
        {
            _context.log("DoS",e);
            ((HttpServletResponse)response).sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
        }
        finally
        {
            if (accepted) //执行完了之后释放当前的信号量,唤醒等待队列中的第一个请求进行处理
            {
                // wake up the next highest priority request.
                for (int p = _queue.length; p-- > 0;)
                {
                    Continuation continuation = _queue[p].poll();
                    if (continuation != null && continuation.isSuspended())
                    {
                        continuation.resume();
                        break;
                    }
                }
                _passes.release();
            }
        }
    }

  public RateTracker getRateTracker(ServletRequest request)
    {
        HttpServletRequest srequest = (HttpServletRequest)request;
        HttpSession session=srequest.getSession(false);

        String loadId = extractUserId(request);
        final int type;
        if (loadId != null)
        {
            type = USER_AUTH;
        }
        else
        {
            if (_trackSessions && session!=null && !session.isNew())
            {
                loadId=session.getId();
                type = USER_SESSION;
            }
            else
            {
                loadId = _remotePort?(request.getRemoteAddr()+request.getRemotePort()):request.getRemoteAddr();
                type = USER_IP;
            }
        }

        RateTracker tracker=_rateTrackers.get(loadId);

        if (tracker==null)
        {
            RateTracker t;
            if (_whitelist.contains(request.getRemoteAddr())) //如果在白名单中,则isRateExceeded一直返回false
            {
                t = new FixedRateTracker(loadId,type,_maxRequestsPerSec);
            }
            else
            {
                t = new RateTracker(loadId,type,_maxRequestsPerSec);
            }

            tracker=_rateTrackers.putIfAbsent(loadId,t);
            if (tracker==null)
                tracker=t;

            if (type == USER_IP)
            {
                // USER_IP expiration from _rateTrackers is handled by the _trackerTimeoutQ
                synchronized (_trackerTimeoutQ)
                {
                    _trackerTimeoutQ.schedule(tracker);
                }
            }
            else if (session!=null)
                // USER_SESSION expiration from _rateTrackers are handled by the HttpSessionBindingListener
                session.setAttribute(__TRACKER,tracker);
        }

        return tracker;
    }
 
分享到:
评论

相关推荐

    用jetty8.0写的websocket实现的简单聊天程序

    在这个“用jetty8.0写的websocket实现的简单聊天程序”中,我们将深入探讨如何利用Jetty 8.0这个轻量级、高性能的Java Web服务器和Servlet容器来构建WebSocket应用。 Jetty是一个开源的HTTP服务器和Servlet容器,...

    jetty实现websocket功能

    Jetty是一个轻量级、高性能的Java Web服务器和Servlet容器,它也支持WebSocket协议,并且集成了一系列WebSocket的API,使得开发者可以轻松地在Java应用中实现WebSocket功能。 在Jetty中实现WebSocket功能,首先你...

    jetty嵌入Web编程多种实现方式案例

    本案例主要探讨Jetty在嵌入式Web编程中的多种实现方法,旨在帮助开发者更好地理解和应用Jetty。 一、Jetty的基本概念与优势 Jetty作为开源项目,其核心设计理念是简洁、快速和可扩展。它支持HTTP、HTTPS、WebSocket...

    Jetty源码分析.pdf

    ### Jetty源码分析知识点概览 #### 一、Jetty简介与特点 - **Jetty**是一款**100%纯Java编写**的轻量级Web服务器与Servlet容器,与Tomcat等其他流行的Web服务器相比,Jetty以其**体积小、启动快**而著称。 - **...

    通过Jetty实现文件上传下载的小工具

    NULL 博文链接:https://vista-rui.iteye.com/blog/1386427

    jetty服务器性能调整过程分析

    ### Jetty服务器性能调整过程分析 #### 一、目标 Jetty服务器采用了非阻塞I/O(NIO)加线程池的技术方案来实现在高并发场景下的高性能表现。本篇文章的目标是通过调整Jetty服务器的各项配置参数,来观察并评估其对...

    jetty相关的全部jar包

    jetty-security-9.4.8.v20171121.jar,jetty-io-9.4.8.v20171121.jar,jetty-continuation-9.4.8.v20171121.jar,jetty-client-9.4.8.v20171121.jar,jetty-jmx-9.4.8.v20171121.jar,jetty-plus-9.4.8.v20171121....

    jetty 8及依赖包

    通过分析源代码和实验,你可以了解到如何自定义配置Jetty以适应特定的应用需求,例如调整线程池大小、添加自定义过滤器或者实现WebSocket端点。 总的来说,这个压缩包是一个极好的起点,无论是对Java Web开发初学者...

    jetty+maven webapp,http,https实现的简单demo

    **Jetty + Maven Webapp 实现HTTP与HTTPS的简要介绍** 在Web应用程序开发中,Jetty是一个轻量级、高性能的嵌入式Java HTTP服务器和Servlet容器。它被广泛用于快速开发、测试和部署Java Web应用。Maven则是一个项目...

    jetty源码剖析

    3. 初始化组件:Jetty 初始化组件,包括 connector、handler、filter 等。 应用加载篇 Jetty 的应用加载过程包括了多个步骤,包括加载 Web 应用、配置应用、初始化应用等。下面是 Jetty 的应用加载过程: 1. 加载...

    jetty内嵌实现

    在"jetty内嵌实现"的场景下,我们可以直接将Jetty集成到我们的Java应用中,避免了传统方式下需要将应用部署到独立的Web服务器(如Tomcat)的步骤。这种方式尤其适用于测试、开发环境,甚至小型生产环境,因为它简化...

    jetty-all.jar

    它包括了异步I/O、NIO和线程池等相关实现,使得Jetty能够高效地处理大量的并发连接。 最后,jetty-webapp-9.4.14.v20181114.jar提供了对Web应用程序的支持。它包含了解析和加载WAR文件、管理Web应用上下文...

    jetty各个版本下载

    Jetty是一款轻量级、高性能的Java Web服务器和Servlet容器,它被广泛应用于各种规模的项目,从小型的个人项目到大型的企业级应用。...无论你是新手还是经验丰富的开发者,Jetty都能提供一个理想的平台来实现你的想法。

    jetty 服务器

    - **Filter和Servlet**:Jetty支持添加自定义Filter和Servlet,以实现特定的Web功能。 - **插件系统**:Jetty的插件系统允许扩展服务器功能,如添加静态文件服务器、部署管理等。 总的来说,Jetty服务器是一个...

    jetty 9.4.9

    1. **Servlet-API.jar**:这是Servlet规范的实现,提供了Servlet、Filter和Listener等核心接口,使得开发者可以编写处理HTTP请求的代码。Servlet API是Java Web应用程序的基础,与Jetty容器协同工作,使得应用能够...

    Jetty权威指南.pdf

    对于高可用性和负载均衡的需求,Jetty支持会话复制,通过在多个Jetty实例之间复制会话数据,实现应用的水平扩展。 #### 十三、性能优化 **13.1 线程池** 合理配置线程池可以显著提升Jetty的性能。通过调整线程池...

    Jetty 学习资料汇总

    1. **线程模型**:分析Jetty的线程池设计,包括Acceptor线程、Selector线程和Worker线程,以及如何优化线程配置。 2. **静态资源服务**:讲解Jetty如何高效地处理静态资源,如HTML、CSS和JavaScript文件。 3. **...

    实战 Jetty--让你快速速学会jetty

    此外,Jetty的轻量化使得将其嵌入到Java应用程序中变得异常简单,只需少量代码即可实现。 在可扩展性方面,Jetty针对高并发和长时间连接的Web 2.0应用进行了优化。它利用Continuation机制有效地处理大量用户请求和...

    i-jetty源码

    i-jetty源码分析 i-jetty是一款基于Java语言实现的轻量级Web服务器和Servlet容器,它在Google Code上可以找到。这个源码库包含了i-jetty项目的版本3.1,提供了对HTTP协议的支持以及对Servlet规范的实现。本文将深入...

    jetty6.1.6-2

    Jetty 6.1.6 是一个开源的、轻量级的Java Web服务器和Servlet容器。这个版本的Jetty发布于较早时期,但它的设计理念和功能仍然对理解Web服务器和Servlet容器的工作原理有很大帮助。Jetty在设计时强调了性能、可嵌入...

Global site tag (gtag.js) - Google Analytics