第一版(点击这儿)写的有点冲冲忙忙,也没有考虑到线程安全方面的问题,在这儿先感谢博友cs6641468的建议,考虑线程安全问题,第二版将HashMap改为了线程安全的ConcurrentHashMap(暂留疑),关于cs6641468提到的filterLimitedIpMap方法,修改为了Task定时进行操作(虽然存在误差,但误差很小的话,基本可以忽略不),另外knight_black_bob博友提到用redis,虽然可以解决,但暂时不想再项目中集成redis。
再次感谢博友的建议。
第二版部分代码:
监听器MyListener:
import java.util.concurrent.ConcurrentHashMap; import javax.servlet.ServletContext; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; /** * @Description 自定义监听器,项目启动时初始化两个全局的ConcurrentHashMap(线程安全) * ipMap(ip存储器,记录IP的访问次数、访问时间) * limitedIpMap(限制IP存储器)用来存储每个访问用户的IP以及访问的次数 * @author zhangyd * @date 2016年7月28日 下午5:47:23 * @since JDK : 1.7 * @version 2.0 * @modify 改hashMap 为 ConcurrentHashMap */ public class MyListener implements ServletContextListener { @Override public void contextInitialized(ServletContextEvent sce) { ServletContext context = sce.getServletContext(); // IP存储器 ConcurrentHashMap<String, Long[]> ipMap = new ConcurrentHashMap<String, Long[]>(); context.setAttribute("ipMap", ipMap); // 限制IP存储器:存储被限制的IP信息 ConcurrentHashMap<String, Long> limitedIpMap = new ConcurrentHashMap<String, Long>(); context.setAttribute("limitedIpMap", limitedIpMap); } @Override public void contextDestroyed(ServletContextEvent sce) { } }
过滤器IPFilter
import java.io.IOException; import java.util.concurrent.ConcurrentHashMap; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.test.util.IPUtil; /** * * @Description 自定义过滤器,用来判断IP访问次数是否超限。<br> * 如果前台用户访问网站的频率过快(比如:达到或超过50次/s),则判定该IP为恶意刷新操作,限制该ip访问<br> * 默认限制访问时间为1小时,一小时后自定解除限制 * * @author zhangyd * @date 2016年7月28日 下午5:54:51 * @since JDK : 1.7 * @version 2.0 * @modify 改hashMap 为 线程安全的ConcurrentHashMap */ public class IPFilter implements Filter { /** 默认限制时间(单位:ms) */ private static final long LIMITED_TIME_MILLIS = 60 * 1000; /** 用户连续访问最高阀值,超过该值则认定为恶意操作的IP,进行限制 */ private static final int LIMIT_NUMBER = 20; /** 用户访问最小安全时间,在该时间内如果访问次数大于阀值,则记录为恶意IP,否则视为正常访问 */ private static final int MIN_SAFE_TIME = 5000; private FilterConfig config; @Override public void init(FilterConfig filterConfig) throws ServletException { this.config = filterConfig; } /** * @Description 核心处理代码 * @param servletRequest * @param servletResponse * @param chain * @throws IOException * @throws ServletException * @see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest, * javax.servlet.ServletResponse, javax.servlet.FilterChain) */ @SuppressWarnings("unchecked") @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) servletRequest; HttpServletResponse response = (HttpServletResponse) servletResponse; ServletContext context = config.getServletContext(); // 获取限制IP存储器:存储被限制的IP信息 ConcurrentHashMap<String, Long> limitedIpMap = (ConcurrentHashMap<String, Long>) context .getAttribute("limitedIpMap"); // 获取用户IP String ip = IPUtil.getIp(request); // 判断是否是被限制的IP,如果是则跳到异常页面 if (isLimitedIP(limitedIpMap, ip)) { long limitedTime = limitedIpMap.get(ip) - System.currentTimeMillis(); forward(request, response, ((limitedTime / 1000) + (limitedTime % 1000 > 0 ? 1 : 0))); return; } // 获取IP存储器 ConcurrentHashMap<String, Long[]> ipMap = (ConcurrentHashMap<String, Long[]>) context.getAttribute("ipMap"); // 判断存储器中是否存在当前IP,如果没有则为初次访问,初始化该ip // 如果存在当前ip,则验证当前ip的访问次数 // 如果大于限制阀值,判断达到阀值的时间,如果不大于[用户访问最小安全时间]则视为恶意访问,跳转到异常页面 if (ipMap.containsKey(ip)) { Long[] ipInfo = ipMap.get(ip); ipInfo[0] = ipInfo[0] + 1; if (ipInfo[0] > LIMIT_NUMBER) { Long ipAccessTime = ipInfo[1]; Long currentTimeMillis = System.currentTimeMillis(); // 限制时间内 if (currentTimeMillis - ipAccessTime <= MIN_SAFE_TIME) { System.out .println(ip + " 在[" + (currentTimeMillis - ipAccessTime) + "]ms内,共访问了[" + ipInfo[0] + "]次"); limitedIpMap.put(ip, currentTimeMillis + LIMITED_TIME_MILLIS); forward(request, response, currentTimeMillis + LIMITED_TIME_MILLIS); return; } else { initIpVisitsNumber(ipMap, ip); } } } else { initIpVisitsNumber(ipMap, ip); } context.setAttribute("ipMap", ipMap); chain.doFilter(request, response); } @Override public void destroy() { } /** * @Description 跳转页面 * @author zhangyd * @date 2016年8月17日 下午5:58:43 * @param request * @param response * @param remainingTime * 剩余限制时间 * @throws ServletException * @throws IOException */ private void forward(HttpServletRequest request, HttpServletResponse response, long remainingTime) throws ServletException, IOException { request.setAttribute("remainingTime", remainingTime); request.getRequestDispatcher("/error/overLimitIP").forward(request, response); } /** * @Description 是否是被限制的IP * @author zhangyd * @date 2016年8月8日 下午5:39:17 * @param limitedIpMap * @param ip * @return true : 被限制 | false : 正常 */ private boolean isLimitedIP(ConcurrentHashMap<String, Long> limitedIpMap, String ip) { if (limitedIpMap == null || limitedIpMap.isEmpty() || ip == null) { // 没有被限制 return false; } return limitedIpMap.containsKey(ip); } /** * 初始化用户访问次数和访问时间 * * @author zhangyd * @date 2016年7月29日 上午10:01:39 * @param ipMap * @param ip */ private void initIpVisitsNumber(ConcurrentHashMap<String, Long[]> ipMap, String ip) { Long[] ipInfo = new Long[2]; ipInfo[0] = 0L;// 访问次数 ipInfo[1] = System.currentTimeMillis();// 初次访问时间 ipMap.put(ip, ipInfo); } }
加入Spring Task定时器。配置文件:applicationContext.xml
<context:component-scan base-package="com.test" > <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller" /> </context:component-scan> <!-- 配置定时任务 --> <task:annotation-driven />
IPFilterTask
import java.util.Date; import java.util.Iterator; import java.util.Map.Entry; import java.util.concurrent.ConcurrentHashMap; import javax.servlet.ServletContext; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import org.springframework.web.context.ContextLoaderListener; /** * @Description IP限制过滤器定时任务,过滤受限的IP,剔除已经到期的限制IP * @author zhangyd * @date 2016年8月18日 上午9:39:19 * @version V1.0 * @since JDK : 1.7 * @modify 将IP限制过滤手动触发改为定时任务 */ @Component public class IPFilterTask { /** * @Description 30s执行一次过滤操作 * @author zhangyd * @date 2016年8月17日 下午5:49:55 */ @Scheduled(cron = "0/30 * * * * ? ") public void filterLimitedIpMap() { ServletContext context = ContextLoaderListener.getCurrentWebApplicationContext().getServletContext(); @SuppressWarnings("unchecked") ConcurrentHashMap<String, Long> limitedIpMap = (ConcurrentHashMap<String, Long>) context .getAttribute("limitedIpMap"); if (limitedIpMap.isEmpty()) { return; } Iterator<Entry<String, Long>> it = limitedIpMap.entrySet().iterator(); long currentTimeMillis = System.currentTimeMillis(); while (it.hasNext()) { Entry<String, Long> e = it.next(); long expireTimeMillis = e.getValue(); if (expireTimeMillis <= currentTimeMillis) { it.remove(); System.out.println(new Date() + "时,去掉了一个限制用户[" + e.getKey() + "]"); } } } }
欢迎各位博友的指正,希望各位博友不吝赐教!
相关推荐
手把手教你学单片机(第二版) 周兴华.pdf
手把手教你学单片机(第二版),汇编版(清晰)。 51单片机入门的经典书籍。书中例子句句有注释。 比C语言更彻底的面向硬件,让人有直接与单片机对话的感觉。 UVZ文件,可有UnicornViewer打开。 UnicornViewer软件...
手把手教你如何建立自己的Linux系统,自己copy下来的做成pdf的,要了解linux一定要跟着做一次
手把手教你学DSP:基于TMS320F28335 手把手教你学DSP:基于TMS320F28335 手把手教你学DSP:基于TMS320F28335 手把手教你学DSP:基于TMS320F28335 手把手教你学DSP:基于TMS320F28335 手把手教你学DSP:基于TMS320F...
这是《手把手教你学51单片机C语言版》的官方电子版,出自www.kingst.org
高清珍藏学习嵌入式开发入门最好资料《手把手教你学51单片机》教材pdf
手把手教你如何建立自己的Linux系统 本教程将手把手教你如何建立自己的Linux系统。
手把手教你学2812,很全面的一本书。电子版,适合初学者学习,
手把手教你学DSP28335高清pdf文件,北京航空航天大学出版社
手把手教你配路由器手把手教你配路由器手把手教你配路由器手把手教你配路由器手把手教你配路由器
手把手教你学28335PDF文档,看了这个确实和2812有了对比
宋雪松先生 《手把手教你学51单片机-C语言版 》 非常贴合实际的单片机教程,感谢宋雪松先生给大家提供优秀的教程
手把手教你DSP配套资料 很有用的资料,用钱买来的资料
手把手教你如何从一无所有到财务自由.pdf
手把手教你学dsp电子版,文档有标签,一本很好的dsp入门书,希望大家一起学习!
手把手教你如何建立自己的Linux系统 第二版(LFS速成手册).pdf
手把手教你学DSP配套资源,里面包含例程和头文件以及配套调试助手
手把手教你学dsp2812,这本书是顾卫刚的,他的讲解比较详细
代码为博客实例代码:http://blog.csdn.net/lmj623565791/article/details/38339817, 有问题请博客留言