请通过浏览器功能收藏网页

Java编写web站点IP防护软核心,全内存操作 ,提供配置化(自己实现的 供大家参考) 博客文章

发布时间:2020-04-29 17:25:00  作者:本站编辑  来源:本站原创  浏览次数:
我有话说 | 分享 |
www.javainfo.com.cn 上干货 欢迎收藏

基于Java编程的防护 可能速度达不到底层代码的速度,只是一个简单的编码实现 供大家鉴赏

项目框架及类分布   模拟页面分布

                                     image.png


开始讲解各个文件的功能:

ChangLiang.java   常量设置 (读取ipcore.properties文件里的配置)


 ipcore.properties  ip计数器的核心属性配置, 字段说明在 ChangLiang.java文件里有详细说明

        urlType=*.do    

        accessCount=10

        hour=1      

        safeIpCheckNum=5

        ipCount_minCount=15

        ipBlack_minCount=20

        ipCheck_minCount=25


public class ChangLiang {
	
	public static void main(String[] args) {
		ChangLiang a = new ChangLiang();
		System.out.println(a);
	}
	
	/**
	 * 标记防护的URL类型   *。jspx
	 */
	public static String urlType;
	/**
	 * 访问次数
	 * <br/>单位时间内访问次数     达到这个值    当前的IP会被加入黑名单
	 */
	public static Integer accessCount;
	/**
	 * 秒     task执行任务频率
	 */
	public static Integer ipCount_minCount;
	public static Integer ipBlack_minCount;
	public static Integer ipCheck_minCount;
	/**
	 * IP封禁小时数,及IP存储在黑名单里的时长,若超过hour小时则 自动解禁
	 */
	public static Integer hour;
	/**
	 * IP安全检查次数  ,若IP检查三次 后    访问量正常   则IPcount 自动清理改IP
	 */
	public static Integer safeIpCheckNum;
	
	public static final SimpleDateFormat sdf = new SimpleDateFormat("YYYY-MM-dd HH:mm:ss");
	
	/**
	 * 获取当前时间点
	 * @return
	 */
	public static String getNowtime(){
		return sdf.format(new Date());
	}
	
	static{
		Properties pro = new Properties();
		FileInputStream in;
		try {
			String path = ChangLiang.class.getResource("/").getFile()+"com/fanghu/core/ipcore.properties";
			in = new FileInputStream(path);
			pro.load(in);
			in.close();
			urlType = pro.getProperty("urlType");
			accessCount = Integer.valueOf(pro.getProperty("accessCount"));
			ipCount_minCount = Integer.valueOf(pro.getProperty("ipCount_minCount"));
			ipBlack_minCount = Integer.valueOf(pro.getProperty("ipBlack_minCount"));
			ipCheck_minCount = Integer.valueOf(pro.getProperty("ipCheck_minCount"));
			hour = Integer.valueOf(pro.getProperty("hour"));
			safeIpCheckNum = Integer.valueOf(pro.getProperty("safeIpCheckNum"));
			
			System.out.println("urlType="+urlType+";accessCount="+accessCount+";ipCount_minCount="+ipCount_minCount+
					";ipBlack_minCount="+ipBlack_minCount+";ipCheck_minCount="+ipCheck_minCount+
					";hour="+hour+";safeIpCheckNum="+safeIpCheckNum );
			
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
}



客户端IP抓取   IPcatch.java:


/**
 * web客户 ip抓取 ,未特殊验证是否满足设计要求,一般直接访问 代理访问 都能拦截
 * @author 此方法来源网络,若有侵权请留言 *
 */
public class IPcatch {

	/** 
     * 获取用户真实IP地址,不使用request.getRemoteAddr()的原因是有可能用户使用了代理软件方式避免真实IP地址, 
     * 可是,如果通过了多级反向代理的话,X-Forwarded-For的值并不止一个,而是一串IP值 
     *  
     * @return ip
     */
    public String getIpAddr(HttpServletRequest request) {
        String ip = request.getHeader("x-forwarded-for"); 
       // System.out.println("x-forwarded-for ip: " + ip);
        if (ip != null && ip.length() != 0 && !"unknown".equalsIgnoreCase(ip)) {  
            // 多次反向代理后会有多个ip值,第一个ip才是真实ip
            if( ip.indexOf(",")!=-1 ){
                ip = ip.split(",")[0];
            }
        }  
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {  
            ip = request.getHeader("Proxy-Client-IP");  
          //  System.out.println("Proxy-Client-IP ip: " + ip);
        }  
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {  
            ip = request.getHeader("WL-Proxy-Client-IP");  
            //System.out.println("WL-Proxy-Client-IP ip: " + ip);
        }  
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {  
            ip = request.getHeader("HTTP_CLIENT_IP");  
            //System.out.println("HTTP_CLIENT_IP ip: " + ip);
        }  
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {  
            ip = request.getHeader("HTTP_X_FORWARDED_FOR");  
           // System.out.println("HTTP_X_FORWARDED_FOR ip: " + ip);
        }  
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {  
            ip = request.getHeader("X-Real-IP");  
            //System.out.println("X-Real-IP ip: " + ip);
        }  
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {  
            ip = request.getRemoteAddr();  
           // System.out.println("getRemoteAddr ip: " + ip);
        } 
       // System.out.println("获取客户端ip: " + ip);
        return ip;  
    }	
}


ip计数器 对于某类访问的客户IP进行技术统计,并读取白名单数据


ipwhite.txt ip白名单  用户避免一些具有网IP或必要的IP被拦截

    127.0.0.1

    127.0.0.3

    0:0:0:0:0:0:0:1


/**
 * IP访问量计数核心  对一类URI进行处理
 * @author WJ
 */
public class IPcount {
	/**
	 * 存储IP白名单 IP , 1 
	 */
	public static Map<String,Integer>  ipWhiteMap = new HashMap<String, Integer>();
	
	static{
		String ip = "";
		String path = ChangLiang.class.getResource("/").getFile()+"com/fanghu/core/ipwhite.txt";
		FileReader f;
		try {
			f = new FileReader(path);
			BufferedReader br = new BufferedReader(f);
			while((ip = br.readLine()) != null){
				ipWhiteMap.put(ip, 1);
			}
			System.out.println("初始化IP白名单OK "+ipWhiteMap.size());
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	public IPcount() {
		super();
	}

	/**
	 * 存储IP计数临时数据 (内存中维护)  IP count
	 */
	public  static Map<String,Integer> ipCountMap = new HashMap<String, Integer>();
	/**
	 * 存储IP黑名单数据   IP  , 1 
	 */
	public static Map<String,Long> ipBlackMap = new HashMap<String, Long>();

	/**
	 * 0 放行;   1 阻止;
	 * <br/>黑名单 不会再记录数目 , 白名单不会记录数目 直接放行
	 * <br/>不属于上面两种的, 计数处理
	 * @param ip
	 * @return
	 */
	public static int process(String ip){
		if(ipWhiteMap.containsKey(ip)) return 0;
		if(ipBlackMap.containsKey(ip)) return 1;
		if(ipCountMap.containsKey(ip)){
			ipCountMap.put(ip, Integer.valueOf(ipCountMap.get(ip) + 1)); 
		}else{
			ipCountMap.put(ip, 1);
		}
		pringIpcount(ipCountMap);
		return 0;
	}
	
	static void pringIpcount(Map<String,Integer> map){
		Iterator<String> it = map.keySet().iterator();
		String ip = null;
		System.out.println("***************************start**************************************************");
		while(it.hasNext()){
			ip = it.next();
			System.out.println("ipCoungMap detail --------------------- "+ip +" count: " + map.get(ip));
		}
		System.out.println("****************************end*************************************************");
	}
	
}



IP动态维护任务 ipCheckingTask.java(核心)


/**
 * IP检查任务,标记哪些IP 应该移入黑名单,这样这个IP池 会自动维护,不用人工干预
 * @author Administrator
 */
public class IpCheckingTask {
	
	public static void IpCheckingMain(){
		try {
			System.out.println("开始启用IP防御核心 。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。");
			ipCountCheck(); //启用IP计数器过滤线程
			ipBlackCheckTask(); //启用黑名单超时释放线程
			ipCountClearTask();  //启用safeIP清理线程
			System.out.println("IP防御核心 。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。启动完成。。");
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * IP校验计时器    IP 校验次数 
	 */
	static Map<String,Integer> checkIpNums = new HashMap<String,Integer>();
	
	//============================================================
	/**
	 * 任务一   单位时间内 访问次数达标的IP 移入 IPBLACK
	 * @throws Exception
	 */
	 public static void ipCountCheck() throws Exception{
	    Runnable runnable = new Runnable() {  
            public void run() {  
            	int count = 0;
            	String ip = null;
            	List<String> toBlackIp = new ArrayList<String>();
            	//System.out.println("ipCountCheck "+ChangLiang.getNowtime());
            	Iterator<String> ipIt = IPcount.ipCountMap.keySet().iterator();
            	while(ipIt.hasNext()){
            		ip = ipIt.next();
            		count = IPcount.ipCountMap.get(ip);
            		System.out.println("ipchecking: count="+count +"   changLiangaccesscount="+ChangLiang.accessCount);
            		if(count >= ChangLiang.accessCount){
            			toBlackIp.add(ip);
            			System.out.println("ipCountMap     toblack ==================================" + ip);
            		}
            		//将校验数据存入历史数据内,用于校验是否是正常的IP访问
            		else{
            			if(checkIpNums.containsKey(ip)){
            				Integer ipSaomiao = checkIpNums.get(ip) + 1;
            				System.out.println("checkIpNums     ==add=====" + ip +" saomiaoNum = "+ipSaomiao);
            				checkIpNums.put(ip, ipSaomiao);
            			}else{
            				checkIpNums.put(ip, 1);
            				System.out.println("checkIpNums     ===init====" + ip +" saomiaoNum = "+1);
            			}
            		}
            	}
            	//System.out.println("校验完毕,开始处理数据,同时清理safeIP计数缓存");
            	if(toBlackIp.size() > 0){
            		 Date now = new Date();
            		for(String t : toBlackIp){
            			IPcount.ipCountMap.remove(t);
            			checkIpNums.remove(t);
            			IPcount.ipBlackMap.put(t, now.getTime());
            			System.out.println("ipCountMap[ipBlackMap checkIpNums]  remove[add remove]   " + t +" timeLong = " + now.getTime());
            		}
            		//System.out.println("IPcount 清理了 IP " + toBlackIp.size() +"个");
            		toBlackIp = null;
            		ipIt = null;
            		now = null;
            		ip = null;
            	}else{
            		System.out.println("ipCountCheck       无须处理 。。。。 IPCOUNT TASK ");
            	}
            }  
        };  
        ScheduledExecutorService service = Executors  
                .newSingleThreadScheduledExecutor();  
        // 第二个参数为首次执行的延时时间,第三个参数为定时执行的间隔时间  单位时间为 S
        service.scheduleAtFixedRate(runnable, 5, ChangLiang.ipCount_minCount, TimeUnit.SECONDS);
     }
	
	  //========================================================
	  //============================================================
		/**
		 * 任务2   黑名单任务 封禁时间达标的IP 自动释放
		 * @throws Exception
		 */
		public static void ipBlackCheckTask() throws Exception{
		    Runnable runnable = new Runnable() {  
	            public void run() {  
	            	//System.out.println("ipBlackCheckTask "+ChangLiang.getNowtime());
	            	Iterator<String> ipBlack =   IPcount.ipBlackMap.keySet().iterator();
	            	String ip=null;
	            	Date now = new Date();
	            	List<String> toDel = new ArrayList<String>();
	            	while(ipBlack.hasNext()){
	            		ip = ipBlack.next();
	            		long toBlackTime = IPcount.ipBlackMap.get(ip);
	            		//测试环境  封存时间按照 S 来计算
	            		//if(( now.getTime() -  ChangLiang.hour * 60 * 1000 ) > toBlackTime){
	            		if(( now.getTime() -  ChangLiang.hour * 3600 * 1000 ) > toBlackTime){
	            			System.out.println("封存时间已经超过了约定的时间,黑名单释放"+ip   +" nowGetTime = "+now.getTime() +" fff="+ChangLiang.hour * 60 +" toBlackTime="+toBlackTime);
	            			toDel.add(ip);
	            		}
	            	}
	            	//System.out.println("黑名单任务校验完毕,开始处理数据");
	            	if(toDel.size() > 0){
	            		for(String t : toDel){
	            			IPcount.ipBlackMap.remove(t);
	            			System.out.println("ipBlackMap 清理了 IP " +t);
	            		}
	            		toDel = null;
	            		now = null;
	            		ipBlack = null;
	            	}else{
	            		System.out.println("ipBlackCheckTask   ------------  无须处理 。。。。  TASK ");
	            	}
	            	
	            }  
	        };  
	        ScheduledExecutorService service = Executors  
	                .newSingleThreadScheduledExecutor();  
	        // 第二个参数为首次执行的延时时间,第三个参数为定时执行的间隔时间  单位时间为 S
	        service.scheduleAtFixedRate(runnable, 5, ChangLiang.ipBlack_minCount, TimeUnit.SECONDS);  
	    }
		//========================================================
		//============================================================
		/**
		 * 为了保持效率 清除正常访问IP<br/>
		 * 任务3 若满足释放条件的IP  清理出IPcount (及可能是正常的访问 非经常访问的IP)
		 * @throws Exception
		 */
		public static void ipCountClearTask() throws Exception{
		    Runnable runnable = new Runnable() {  
	            public void run() {  
	            	System.out.println("ipCountClearTask "+ChangLiang.getNowtime());
	            	Iterator<String> ipIt =  checkIpNums.keySet().iterator();
	            	String ip = null;
	            	List<String> safeIp = new ArrayList<String>();
	            	while(ipIt.hasNext()){
	            		ip = ipIt.next();
	            		System.out.println("safeIpcheck  IP= "+ip +" 安全校验次数 "+checkIpNums.get(ip) +" ; 安全校验标准次数  "+ ChangLiang.safeIpCheckNum);
	            		if(checkIpNums.get(ip) >= ChangLiang.safeIpCheckNum){
	            			safeIp.add(ip);
	            		}
	            	}
	            	//System.out.println("safeIP 数据处理完成,开始清理 ");
	            	if(safeIp.size() > 0){
	            		for(String si : safeIp){
	            			checkIpNums.remove(si);
	            			IPcount.ipCountMap.remove(si);
	            			System.out.println("安全IP释放 IPcount[checkIpNums] remove[remove]= "+si);
	            		}
	            	//	System.out.println("ipCount safeIp清理完成 " + safeIp.size());
	            		safeIp = null;
	            		ip = null;
	            		ipIt = null;
	            	}else{
	            		System.out.println("safeIp 无须清理 ,没有满足条件的IP ");
	            	}
	            	IPcount.pringIpcount(IPcount.ipCountMap);
	            }  
	        };  
	        ScheduledExecutorService service = Executors  
	                .newSingleThreadScheduledExecutor();  
	        // 第二个参数为首次执行的延时时间,第三个参数为定时执行的间隔时间  单位时间为 S
	        service.scheduleAtFixedRate(runnable, 10, ChangLiang.ipCheck_minCount, TimeUnit.SECONDS);  
	    }
}


IPcheckFilter.java     核心部署 依赖web的过滤器FILTER,上代码  哪些操作频繁需要防护可以在过滤器上调整

@WebFilter("/*")
public class IpCheckFilter implements Filter {
	
	private IPcatch ipCatch = null;

    /**
     * Default constructor. 
     */
    public IpCheckFilter() {
        // TODO Auto-generated constructor stub
    }

	/**
	 * @see Filter#destroy()
	 */
	public void destroy() {
		// TODO Auto-generated method stub
	}

	/**
	 * @see Filter#doFilter(ServletRequest, ServletResponse, FilterChain)
	 */
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
		// TODO Auto-generated method stub
		// place your code here
		//特殊请求 特殊处理,如提示访问频繁的界面
		HttpServletRequest req =   (HttpServletRequest)request;
		String url =  req.getRequestURL().toString();
		System.out.println("url: ------------- = "+url);
		if(url.indexOf("res403") > 0){
			chain.doFilter(request, response);
			return;
		}
		//截取访问客户端的IP
		String webUserIp = ipCatch.getIpAddr(req);
		System.out.println("filterWebUserIp=" + webUserIp);
		//IP通过防护核心的IP计数器 记录一下
		int rsInt =  IPcount.process(webUserIp);
		
		if(rsInt == 0){
		        //防护核心认为 可以通行
			chain.doFilter(request, response);
		}else{
		        //未通过防护核心
			HttpServletResponse res = (HttpServletResponse) response;
			res.setStatus(403);
			request.getRequestDispatcher("/res403.jsp").forward(request, response);
		}
	}

	/**
	 * @see Filter#init(FilterConfig)
	 */
	public void init(FilterConfig fConfig) throws ServletException {
		// TODO Auto-generated method stub
		//启动IP防御核心
		IpCheckingTask.IpCheckingMain();
		//启动IP抓取功能
		ipCatch = new IPcatch();
		System.out.println("filter OK ");
		System.out.println("IPcatch process OK ");
		
	}

}


全内存操作 速度还可以

测试打印如下,可以正常提供拦截服务

初始化IP白名单OK 3
url: ------------- = http://192.168.2.222/
filterWebUserIp=192.168.2.222
ipCoungMap detail --------------------- 192.168.2.222 count: 1
ipchecking: count=1   changLiangaccesscount=10
checkIpNums     ===init====192.168.2.222 saomiaoNum = 1
ipCountCheck       无须处理 。。。。 IPCOUNT TASK 
ipBlackCheckTask   ------------  无须处理 。。。。  TASK 
url: ------------- = http://192.168.2.222/
filterWebUserIp=192.168.2.222
ipCoungMap detail --------------------- 192.168.2.222 count: 2
url: ------------- = http://192.168.2.222/
filterWebUserIp=192.168.2.222
ipCoungMap detail --------------------- 192.168.2.222 count: 3
url: ------------- = http://192.168.2.222/
filterWebUserIp=192.168.2.222
ipCoungMap detail --------------------- 192.168.2.222 count: 4
url: ------------- = http://192.168.2.222/
filterWebUserIp=192.168.2.222
ipCoungMap detail --------------------- 192.168.2.222 count: 5
url: ------------- = http://192.168.2.222/
filterWebUserIp=192.168.2.222
ipCoungMap detail --------------------- 192.168.2.222 count: 6
url: ------------- = http://192.168.2.222/
filterWebUserIp=192.168.2.222
ipCoungMap detail --------------------- 192.168.2.222 count: 7
ipCountClearTask 2020-04-29 14:27:34
safeIpcheck  IP= 192.168.2.222 安全校验次数 1 ; 安全校验标准次数  5
safeIp 无须清理 ,没有满足条件的IP 
url: ------------- = http://192.168.2.222/
filterWebUserIp=192.168.2.222
ipCoungMap detail --------------------- 192.168.2.222 count: 8
url: ------------- = http://192.168.2.222/
filterWebUserIp=192.168.2.222
ipCoungMap detail --------------------- 192.168.2.222 count: 9
url: ------------- = http://192.168.2.222/
filterWebUserIp=192.168.2.222
ipCoungMap detail --------------------- 192.168.2.222 count: 10
url: ------------- = http://192.168.2.222/
filterWebUserIp=192.168.2.222
ipCoungMap detail --------------------- 192.168.2.222 count: 11
url: ------------- = http://192.168.2.222/
filterWebUserIp=192.168.2.222
ipCoungMap detail --------------------- 192.168.2.222 count: 12
url: ------------- = http://192.168.2.222/
filterWebUserIp=192.168.2.222
ipCoungMap detail --------------------- 192.168.2.222 count: 13
url: ------------- = http://192.168.2.222/
filterWebUserIp=192.168.2.222
ipCoungMap detail --------------------- 192.168.2.222 count: 14
ipchecking: count=14   changLiangaccesscount=10
ipCountMap     toblack ==================================192.168.2.222
ipCountMap[ipBlackMap checkIpNums]  remove[add remove]   192.168.2.222 timeLong = 1588141664184
ipBlackCheckTask   ------------  无须处理 。。。。  TASK 
ipCountCheck       无须处理 。。。。 IPCOUNT TASK 
ipCountClearTask 2020-04-29 14:27:59
safeIp 无须清理 ,没有满足条件的IP 
ipBlackCheckTask   ------------  无须处理 。。。。  TASK 
ipCountCheck       无须处理 。。。。 IPCOUNT TASK 
ipCountClearTask 2020-04-29 14:28:24
safeIp 无须清理 ,没有满足条件的IP 
ipCountCheck       无须处理 。。。。 IPCOUNT TASK 
ipBlackCheckTask   ------------  无须处理 。。。。  TASK 
ipCountCheck       无须处理 。。。。 IPCOUNT TASK 
封存时间已经超过了约定的时间,黑名单释放192.168.2.222 nowGetTime = 1588141729186 fff=60 toBlackTime=1588141664184
ipBlackMap 清理了 IP 192.168.2.222
ipCountClearTask 2020-04-29 14:28:49
safeIp 无须清理 ,没有满足条件的IP 
ipCountCheck       无须处理 。。。。 IPCOUNT TASK 
ipBlackCheckTask   ------------  无须处理 。。。。  TASK


如有疑问 请留言 欢迎提供建议

评论已有 0