java 实现IP访问量控制

同一个IP 访问网站, 不能同时超过配置的最大值, 可以用来保护REST IP 或者DOS攻击

 

/*
 * Pprun's Public Domain.
 */
package org.pprun.common.security;

import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Required;
import org.springframework.jmx.export.annotation.ManagedAttribute;
import org.springframework.scheduling.annotation.Scheduled;

/**
 * A filter to throttle only a limited number of requests from the same ip per second.
 * <p>
 * Two parameters need to inject:
 * <ol>
 * <li>common.throttle.maxConcurrentRequests=10</li>
 * <li>common.throttle.period=1000</li>
 * </ol>
 * <br />
 * If you use {@literal Spring} for {@lit DI}, it can be done as below in application {@literal web.xml}:
 * <br />
 * {@code 
 *      <filter>  
 *          <description>A filter to throttle only a limited number of requests from the same ip per second.</description>  
 *          <filter-name>throttleFilter</filter-name>  
 *          <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>  
 *      </filter>
 * }
 * <br />
 * And a bean with {@code id="throttleFilter"} should be defined in {@literal application.xml}.
 * </p>
 * @author <a href="mailto:pizhigang@letv.com">pizhigang</a>
 */
//@Component
public class ThrottleFilter implements Filter, ThrottleFilterMXBean {

    private static Logger log = LoggerFactory.getLogger(ThrottleFilter.class);
    // map(ip, requestCount)
    private Map<String, Integer> ip2countCache = new HashMap<String, Integer>();
    private Set<String> blackList = new HashSet<String>();
    private int maxConcurrentRequests;
    private static final long PERIOD = 1L; // second
    private boolean enable = false;

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

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain nextFilter) throws ServletException, IOException {
        if (enable) {
            final String ip = request.getRemoteAddr();
            boolean isOverflow;

            synchronized (this) {
                Integer count = ip2countCache.get(ip);

                if (count == null || count.intValue() == 0) {
                    count = 0;
                }

                if (count < maxConcurrentRequests) {
                    isOverflow = false;
                    ip2countCache.put(ip, count + 1);
                } else {
                    isOverflow = true;
                    blackList.add(ip);
                }
            }

            if (isOverflow) {
                // block it
                log.info(" ip {} has reached the threshold {} in {} second, block it!", new Object[]{ip, maxConcurrentRequests, PERIOD});

                if (response instanceof HttpServletResponse) {
                    ((HttpServletResponse) response).sendError(503, ip + " has too many concurrent requests per " + PERIOD + " second");
                }
                return;
            }
        } // else go ahead below

        // every thing is okay, go ahead
        nextFilter.doFilter(request, response);
    }

    // every 1 second
    @Scheduled(fixedRate = PERIOD * 1000)
    public void throttlingJob() {
        if (enable) {
            log.debug("Throttle Filter clean up job is running");
            synchronized (ThrottleFilter.this) {
                for (Map.Entry<String, Integer> ip2count : ip2countCache.entrySet()) {
                    Integer count = ip2count.getValue();
                    String ip = ip2count.getKey();

                    if (count == null || count <= 1) {
                        ip2countCache.remove(ip);
                    } else {
                        if (count == maxConcurrentRequests) {
                            // remove from blacklist
                            log.info("Throttle Filter: removing {} from black list", ip);
                            blackList.remove(ip);
                        }

                        ip2countCache.put(ip, count - 1);
                    }
                }
            }
            log.debug("Throttle Filter clean up job is done");
        }
    }

    /**
     * Any cleanup for the filter.
     */
    @Override
    public void destroy() {
        log.warn("destorying Throttle Filter");
    }

    /**
     * Sets the maximum number of concurrent requests for a single IP.
     */
    @Required
    public void setMaxConcurrentRequests(int max) {
        maxConcurrentRequests = max;
    }

    @ManagedAttribute(description = "is the throttle filter enable or not")
    @Override
    public boolean isEnable() {
        return enable;
    }

    @Required
    @Override
    public void setEnable(boolean enable) {
        log.info("set enable to {}", enable);
        if (enable == false) {
            log.info("Throttle filter will be disabled");
        }

        this.enable = enable;
    }

    public Set<String> getBlackList() {
        // for exactly, it might need synchronized, but no hurt for snapshoot in one or severl seconds
        return Collections.unmodifiableSet(blackList);
    }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值