編寫一個過濾器:
package org.jeemp.config;import java.io.IOException;import java.util.Iterator;import java.util.Map;import java.util.Set;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.annotation.WebFilter;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import org.jeemp.utils.IpUtils;/** * @author JackRen * @date 2021-02-27 14:51 * @description: * 自定義過濾器,用來判斷IP訪問次數是否超限 * 如果前臺使用者訪問網站的頻率過快(達到超過50次/秒),則判定該ip惡意重新整理操作, * 限制該IP的訪問,1小時後自己解除限制 */@WebFilter(urlPatterns="/*")public class IpFilter implements Filter { /** * 預設限制時間(單位:ms) */ private static final long LIMITED_TIME_MILLIS = 60 * 60 * 1000; /** * 使用者連續訪問最高閥值,超過該值則認定為惡意操作的IP,進行限制 */ private static final int LIMIT_NUMBER = 50; /** * 使用者訪問最小安全時間,在該時間內如果訪問次數大於閥值,則記錄為惡意IP,否則視為正常訪問 */ private static final int MIN_SAFE_TIME = 5000; private FilterConfig config; @Override public void init(FilterConfig filterConfig) throws ServletException { this.config = filterConfig; //設定屬性filterConfig } /* (non-Javadoc) * @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資訊 Map<String, Long> limitedIpMap = (Map<String, Long>) context.getAttribute("limitedIpMap"); // 過濾受限的IP filterLimitedIpMap(limitedIpMap); // 獲取使用者IP String ip = IpUtils.getRemoteHost(request); System.err.println("ip:"+ip); // 判斷是否是被限制的IP,如果是則跳到異常頁面 if (isLimitedIP(limitedIpMap, ip)) { long limitedTime = limitedIpMap.get(ip) - System.currentTimeMillis(); // 剩餘限制時間(用為從毫秒到秒轉化的一定會存在些許誤差,但基本可以忽略不計) request.setAttribute("remainingTime", ((limitedTime / 1000) + (limitedTime % 1000 > 0 ? 1 : 0))); //request.getRequestDispatcher("/error/overLimitIP").forward(request, response); System.err.println("ip訪問過於頻繁:"+ip); //response.setCharacterEncoding("gb2312"); //設定輸出內容編碼格式// response.reset();// response.setCharacterEncoding("utf-8"); //設定輸出內容編碼格式// PrintWriter out = response.getWriter();// out.println("<b>由於您訪問過於頻繁,被系統自動認定為機器人。1個小時自動解除</b>");、 return; } // 獲取IP儲存器 Map<String, Long[]> ipMap = (Map<String, Long[]>) context.getAttribute("ipMap"); // 判斷儲存器中是否存在當前IP,如果沒有則為初次訪問,初始化該ip // 如果存在當前ip,則驗證當前ip的訪問次數 // 如果大於限制閥值,判斷達到閥值的時間,如果不大於[使用者訪問最小安全時間]則視為惡意訪問,跳轉到異常頁面 if (ipMap.containsKey(ip)) { Long[] ipInfo = ipMap.get(ip); ipInfo[0] = ipInfo[0] + 1; System.out.println("當前第[" + (ipInfo[0]) + "]次訪問"); if (ipInfo[0] > LIMIT_NUMBER) { Long ipAccessTime = ipInfo[1]; Long currentTimeMillis = System.currentTimeMillis(); if (currentTimeMillis - ipAccessTime <= MIN_SAFE_TIME) { limitedIpMap.put(ip, currentTimeMillis + LIMITED_TIME_MILLIS); request.setAttribute("remainingTime", LIMITED_TIME_MILLIS); System.err.println("ip訪問過於頻繁:"+ip); request.getRequestDispatcher("/error/overLimitIP").forward(request, response); return; } else { initIpVisitsNumber(ipMap, ip); } } } else { initIpVisitsNumber(ipMap, ip); System.out.println("您首次訪問該網站"); } context.setAttribute("ipMap", ipMap); chain.doFilter(request, response); } @Override public void destroy() { // TODO Auto-generated method stub } /** * @Description 過濾受限的IP,剔除已經到期的限制IP * @param limitedIpMap */ private void filterLimitedIpMap(Map<String, Long> limitedIpMap) { if (limitedIpMap == null) { return; } Set<String> keys = limitedIpMap.keySet(); Iterator<String> keyIt = keys.iterator(); long currentTimeMillis = System.currentTimeMillis(); while (keyIt.hasNext()) { long expireTimeMillis = limitedIpMap.get(keyIt.next()); if (expireTimeMillis <= currentTimeMillis) { keyIt.remove(); } } } /** * @Description 是否是被限制的IP * @param limitedIpMap * @param ip * @return true : 被限制 | false : 正常 */ private boolean isLimitedIP(Map<String, Long> limitedIpMap, String ip) { if (limitedIpMap == null || ip == null) { // 沒有被限制 return false; } Set<String> keys = limitedIpMap.keySet(); Iterator<String> keyIt = keys.iterator(); while (keyIt.hasNext()) { String key = keyIt.next(); if (key.equals(ip)) { // 被限制的IP return true; } } return false; } /** * 初始化使用者訪問次數和訪問時間 * * @param ipMap * @param ip */ private void initIpVisitsNumber(Map<String, Long[]> ipMap, String ip) { Long[] ipInfo = new Long[2]; ipInfo[0] = 0L;// 訪問次數 ipInfo[1] = System.currentTimeMillis();// 初次訪問時間 ipMap.put(ip, ipInfo); }}
建立一個監聽器:需要初始化兩個容器package org.jeemp.config;import java.util.HashMap;import java.util.Map;import javax.servlet.ServletContext;import javax.servlet.ServletContextEvent;import javax.servlet.ServletContextListener;import javax.servlet.annotation.WebListener;import org.slf4j.Logger;import org.slf4j.LoggerFactory;/** * @author JackRen * @date 2021-02-27 14:56 * @description: */public class MyListener implements ServletContextListener{ private Logger logger = LoggerFactory.getLogger(MyListener.class); @Override public void contextInitialized(ServletContextEvent sce) { logger.info("liting: contextInitialized"); System.err.println("MyListener初始化成功"); ServletContext context = sce.getServletContext(); // IP儲存器 Map<String, Long[]> ipMap = new HashMap<String, Long[]>(); context.setAttribute("ipMap", ipMap); // 限制IP儲存器:儲存被限制的IP資訊 Map<String, Long> limitedIpMap = new HashMap<String, Long>(); context.setAttribute("limitedIpMap", limitedIpMap); logger.info("ipmap:"+ipMap.toString()+";limitedIpMap:"+limitedIpMap.toString()+"初始化成功。。。。。"); } @Override public void contextDestroyed(ServletContextEvent sce) { // TODO Auto-generated method stub }}
啟動類裡面新增以上監聽器和過濾器的掃描包路徑@ServletComponentScan(basePackages="org.jeemp")
最新評論