懈怠篇,不想写字,间接贴代码

采纳拦截器的形式拦挡申请并计算,拦挡规定是在X时间段内收到超过Y次申请,就断定为攻打,申请又分为失常申请异样申请
这里为了简化组件引入老本,采纳内存存储的形式,如果有须要能够改为其余存储形式(如redis)。申请次数采纳本地缓存(guava)+自定义的滑动窗口(看后面的文章)来存储和计算。

@Slf4jpublic class AttackInterceptor extends HandlerInterceptorAdapter {    private Limiter limiter;    private ConcurrentHashSet<String> blockIps;    @Value("#{new Boolean('${web.attackInterceptor.allowTimes.enable:false}')}")    private boolean allowTimesAttackInterceptor;    @Value("#{new Boolean('${web.attackInterceptor.errorAllowTimes.enable:true}')}")    private boolean errorAllowTimesAttackInterceptor;    @Value("${web.attackInterceptor.windowIntervalInMs:1000}")    private int windowIntervalInMs;    @Value("${web.attackInterceptor.allowTimes:20}")    private int allowTimes;    @Value("${web.attackInterceptor.errorAllowTimes:10}")    private int errorAllowTimes;    @Value("#{'${web.attackInterceptor.excludeFilterPath:}'.isEmpty() ? null : '${web.attackInterceptor.excludeFilterPath:}'.split(',')}")    private List<String> excludeFilterPath;    @PostConstruct    public void init() {        limiter = new Limiter(windowIntervalInMs);        blockIps = new ConcurrentHashSet<>();        if (excludeFilterPath == null) {            excludeFilterPath = new ArrayList<>();        }        excludeFilterPath.add("/removeAttackBlackIp");        excludeFilterPath.add("/addAttackBlackIp");    }    @Override    public boolean preHandle(HttpServletRequest request, HttpServletResponse response,            Object handler) {        String ip = HttpIpUtils.getRealIP(request);        try {            if (doLimited(request, response)) {                return false;            }            if (allowTimesAttackInterceptor && !limiter.get(ip).tryAcquire(AttackType.REQ, allowTimes)) {                log.error("[AttackInterceptor]{}每秒申请超过限度次数,将被限度申请!", ip);                blockIps.add(ip);            }            if (doLimited(request, response)) {                return false;            }        } catch (Exception e) {            log.error("AttackInterceptor preHandle [url:{}, ip:{}] error!", request.getRequestURL().toString(), ip, e);        }        return true;    }    @Override    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,            Exception ex) {        String ip = HttpIpUtils.getRealIP(request);        try {            int responseStatus = response.getStatus();            if (!HttpStatus.valueOf(responseStatus).isError()) {                return;            }            if (errorAllowTimesAttackInterceptor && !limiter.get(ip)                    .tryAcquire(AttackType.ERROR_REQ, errorAllowTimes - 1)) {                log.error("[AttackInterceptor]{}每秒返回异样的申请超过限度次数,将被限度申请!", ip);                blockIps.add(ip);            }        } catch (Exception e) {            log.error("AttackInterceptor afterCompletion [url:{}, ip:{}] error!", request.getRequestURL().toString(),                    ip, e);        }    }    /**     * @param request     * @param response     * @return boolean     * @author      * @date      */    private boolean doLimited(HttpServletRequest request, HttpServletResponse response) {        if (!allowTimesAttackInterceptor && !errorAllowTimesAttackInterceptor) {            return false;        }        String uri = request.getRequestURI();        if (excludeFilterPath.contains(uri)) {            return false;        }        String ip = HttpIpUtils.getRealIP(request);        if (blockIps.contains(ip)) {            writeResult(response);            log.warn("[url:{}, ip:{}] is blocked by AttackInterceptor!", request.getRequestURL().toString(), ip);            return true;        }        return false;    }    /**     * @param response     * @author      * @date      */    protected void writeResult(HttpServletResponse response) {        PrintWriter writer = null;        response.setStatus(HttpStatus.FORBIDDEN.value());        response.setCharacterEncoding("UTF-8");        response.setContentType("application/json;charset=UTF-8");        try {            writer = response.getWriter();            writer.print(JSON.toJSONString(ResponseResult.fail("blocked!")));            writer.flush();        } catch (Exception e) {            log.error("AttackInterceptor write blocked error!", e);        } finally {            if (writer != null) {                writer.close();            }        }    }    /**     * @param ip     * @return boolean     * @author      * @date      */    public boolean removeBlockIp(String ip) {        return blockIps.remove(ip);    }    /**     * @param ip     * @return boolean     * @author      * @date      */    public boolean addBlockIp(String ip) {        if (blockIps.contains(ip)) {            return false;        }        return blockIps.add(ip);    }    enum AttackType {        REQ,        ERROR_REQ    }    /**     * @author      * @return null     * @date     */    class Limiter extends AbstractLocalCache<String, SlidingWindow> {        public static final int DEFAULT_CACHE_MAX_SIZE = 1000;        public static final int DEFAULT_CACHE_EXPIRE = 360;        private int windowIntervalInMs;        public Limiter(int windowIntervalInMs) {            super(CacheBuilder.newBuilder().maximumSize(DEFAULT_CACHE_MAX_SIZE)                    .expireAfterAccess(DEFAULT_CACHE_EXPIRE, TimeUnit.SECONDS));            this.windowIntervalInMs = windowIntervalInMs;        }        @Override        protected SlidingWindow loadData(String key) {            return new SlidingWindow.SlidingWindowBuilder()                    .ofEventTypeClass(AttackType.class)                    .ofBucketCount(10)                    .ofWindowIntervalInMs(windowIntervalInMs)                    .build();        }    }}

减少手动增加/删除IP黑名单的接口。为了平安起见,增加是否容许手动操作的开关,惯例工夫倡议敞开开关

@RestController@RequestMapping("")public class WebController {    @Value("${web.attackInterceptor.canOpt:false}")    private boolean classOptAttackBlackIp;    @Autowired    private AttackInterceptor attackInterceptor;    /**     * @param ip     * @return java.lang.String     * @author      * @date      */    @RequestMapping(value = "/removeAttackBlackIp",            method = {RequestMethod.GET, RequestMethod.POST})    public String removeAttackBlackIp(String ip) {        if (!classOptAttackBlackIp) {            return "禁止手动操作";        }        if (attackInterceptor.removeBlockIp(ip)) {            return ip + "曾经从[AttackInterceptor]黑名单中移除!";        }        return ip + "不在[AttackInterceptor]黑名单中!";    }    /**     * @param ip     * @return java.lang.String     * @author      * @date      */    @RequestMapping(value = "/addAttackBlackIp",            method = {RequestMethod.GET, RequestMethod.POST})    public String addAttackBlackIp(String ip) {        if (!classOptAttackBlackIp) {            return "禁止手动操作";        }        if (attackInterceptor.addBlockIp(ip)) {            return ip + "已增加到[AttackInterceptor]黑名单中!";        }        return ip + "曾经存在于[AttackInterceptor]黑名单中!";    }}

配置Bean

@Configuration@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)@ConditionalOnClass({Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class})public class WebConfiguration implements WebMvcConfigurer {    @Bean    public AttackInterceptor getAttackInterceptor() {        return new AttackInterceptor();    }    @Override    public void addInterceptors(InterceptorRegistry registry) {        InterceptorRegistration registration = registry.addInterceptor(new LogInterceptor());        registration.excludePathPatterns("/error", "/druid/*", "/webjars/**", "/*.js", "/*.html",                "/*.img", "/swagger-resources/**", "/webjars/**", "/v2/**",                "/swagger-ui.html/**", "/csrf");        registry.addInterceptor(getAttackInterceptor());    }}