Java学习者论坛

 找回密码
 立即注册

QQ登录

只需一步,快速开始

手机号码,快捷登录

恭喜Java学习者论坛(https://www.javaxxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,购买链接:点击进入购买VIP会员
JAVA高级面试进阶视频教程Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程

Go语言视频零基础入门到精通

Java架构师3期(课件+源码)

Java开发全终端实战租房项目视频教程

SpringBoot2.X入门到高级使用教程

大数据培训第六期全套视频教程

深度学习(CNN RNN GAN)算法原理

Java亿级流量电商系统视频教程

互联网架构师视频教程

年薪50万Spark2.0从入门到精通

年薪50万!人工智能学习路线教程

年薪50万!大数据从入门到精通学习路线年薪50万!机器学习入门到精通视频教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程 MySQL入门到精通教程
查看: 382|回复: 0

[jsp学习]Java Web中的入侵检测及简单实现

[复制链接]
  • TA的每日心情
    开心
    2021-3-12 23:18
  • 签到天数: 2 天

    [LV.1]初来乍到

    发表于 2014-10-1 17:22:30 | 显示全部楼层 |阅读模式
    一、简介

    Java Web应用程中,特别是网站开发中,我们有时候需要为应用程序增加一个入侵检测程序来防止恶意刷新的功能,防止非法用户不断的往Web应用中重复发送数据。当然,入侵检测可以用很多方法实现,包括软件、硬件防火墙,入侵检测的策略也很多。在这里我们主要介绍的是java Web应用程序中通过软件的方式实现简单的入侵检测及防御。  该方法的实现原理很简单,就是用户访问Web系统时记录每个用户的信息,然后进行对照,并根据设定的策略(比如:1秒钟刷新页面10次)判断用户是否属于恶意刷新。  我们的入侵检测程序应该放到所有Java Web程序的执行前,也即若发现用户是恶意刷新就不再继续执行Java Web中的其它部分内容,否则就会失去了意义。这就需要以插件的方式把入侵检测的程序置入Java Web应用中,使得每次用户访问Java Web,都先要到这个入侵检测程序中报一次到,符合规则才能放行。  
      
       Java Web应用大致分为两种,一种纯JSP(+Java Bean)方式,一种是基于框架(如Struts、EasyJWeb等)的。第一种方式的Java Web可以通过Java Servlet中的Filter接口实现,也即实现一个Filter接口,在其doFilter方法中插入入侵检测程序,然后再web.xml中作简单的配置即可。在基于框架的Web应用中,由于所有应用都有一个入口,因此可以把入侵检测的程序直接插入框架入口引擎中,使框架本身支持入侵检测功能。当然,也可以通过实现Filter接口来实现。  在EasyJWeb框架中,已经置入了简单入侵检测的程序,因此,这里我们以EasyJWeb框架为例,介绍具体的实现方法及源码,完整的代码可以在EasyJWeb源码中找到。  在基于EasyJWeb的Java Web应用中(如http://www.easyjf.com/bbs/),默认情况下你只要连续刷新页面次数过多,即会弹出如下的错误:  EasyJWeb框架友情提示!:-):
      您对页面的刷新太快,请等待60秒后再刷新页面!
    详细请查询http://www.easyjf.com/
    二、用户访问信息记录UserConnect.java类

    这个类是一个简单的Java Bean,主要代表用户的信息,包括用户名、IP、第一次访问时间、最后登录时间、登录次数、用户状态等。全部

    代码如下:

    package com.easyjf.web; import java.util.Date;
    /**
    *
    * <p>Title:用户验证信息</p>
    * <p>Description:记录用户登录信息,判断用户登录情况</p>
    * <p>Copyright: Copyright (c) 2006</p>
    * <p>Company: www.easyjf.com</p>
    * @author 蔡世友
    * @version 1.0
    */
    public class UserConnect {
    private String userName;
    private String ip;
    private Date firstFailureTime;
    private Date lastLoginTime;
    private int failureTimes;//用户登录失败次数
    private int status=0;//用户状态0表示正常,-1表示锁定
    public int getFailureTimes() {
      return failureTimes;
    }
    public void setFailureTimes(int failureTimes) {
      this.failureTimes = failureTimes;
    }
    public Date getFirstFailureTime() {
      return firstFailureTime;
    } public void setFirstFailureTime(Date firstFailureTime) {
      this.firstFailureTime = firstFailureTime;
    } public String getIp() {
      return ip;
    } public void setIp(String ip) {
      this.ip = ip;
    } public Date getLastLoginTime() {
      return lastLoginTime;
    } public void setLastLoginTime(Date lastLoginTime) {
      this.lastLoginTime = lastLoginTime;
    } public String getUserName() {
      return userName;
    } public void setUserName(String userName) {
      this.userName = userName;
    } public int getStatus() {
      return status;
    } public void setStatus(int status) {
      this.status = status;
    } }
    三、监控线程UserConnectManage.java类

    这是入侵检测的核心部分,主要实现具体的入侵检测、记录、判断用户信息、在线用户的刷新等功能,并提供其它应用程序使用本组件的调用接口。

    package com.easyjf.web; import java.util.Date;
    import java.util.HashMap;
    import java.util.HashSet;
    import java.util.Iterator;
    import java.util.Map;
    import java.util.Set; import org.apache.log4j.Logger;
    /**
    *
    * <p>Title:用户入侵检测信息</p>
    * <p>Description:用于判断用户刷新情况检查,默认为10秒钟之内连续连接10次为超时</p>
    * <p>Copyright: Copyright (c) 2006</p>
    * <p>Company: www.easyjf.com</p>
    * @author 蔡世友
    * @version 1.0
    */
    public class UserConnectManage {
    private static final Logger logger = (Logger) Logger.getLogger(UserConnectManage.class.getName());
    private static int maxFailureTimes=10;//最大登录失败次数
    private static long maxFailureInterval=10000;//毫秒,达到最大登录次数且在这个时间范围内
    private static long waitInterval=60000;//失败后接受连接的等待时间,默认1分钟
    private static int maxOnlineUser=200;//同时在线的最大数
    private final static Map users=new HashMap();//使用ip+userName为key存放用户登录信息UserLoginAuth
    private static Thread checkThread=null;
    private static class CheckTimeOut implements Runnable{
    private Thread parentThread;
    public  CheckTimeOut(Thread parentThread)
      {
       this.parentThread=parentThread;
       synchronized(this){
       if(checkThread==null){   
        checkThread= new Thread(this);
        //System.out.println("创建一个新线程!");
        checkThread.start();   
          }
       }
      }
      public void run() {  
       while(true)
       {
        if(parentThread.isAlive()){
        try{
        Thread.sleep(2000);
        int i=0;
        if(users.size()>maxOnlineUser)//当达到最大用户数时清除
        {
         synchronized(users){//执行删除操作
         Iterator it=users.keySet().iterator();
         Set set=new HashSet();
         Date now=new Date();
         while(it.hasNext())
         {
          Object key=it.next();
          UserConnect user=(UserConnect)users.get(key);
          if(now.getTime()-user.getFirstFailureTime().getTime()>maxFailureInterval)//删除超时的用户
          {      
           set.add(key);
           logger.info("删除了一个超时的连接"+i);
           i++;
          }
         }
         if(i<5)//如果删除少于5个,则强行删除1/2在线记录,牺牲性能的情况下保证内存
         {
          int num=maxOnlineUser/2;
          it=users.keySet().iterator();
          while(it.hasNext() && i<num)
          {      
           set.add(it.next());
           logger.info("删除了一个多余的连接"+i);
           i++;
          }
         }
         users.keySet().removeAll(set);
         }
        }
       
        }
        catch(Exception e)
        {
         e.printStackTrace();
        }
       
       }
        else
        {   
        break;
        }
       }
       logger.info("监视程序运行结束!");
      }
    }
    //通过checkLoginValidate判断是否合法的登录连接,如果合法则继续,非法则执行
    public static boolean checkLoginValidate(String ip,String userName)//只检查认证失败次数
    {
      boolean ret=true;
      Date now=new Date();
      String key=ip+":"+userName;
      UserConnect auth=(UserConnect)users.get(key);
      if(auth==null)//把用户当前的访问信息加入到users容器中
      {
       auth=new UserConnect();
       auth.setIp(ip);
       auth.setUserName(userName);
       auth.setFailureTimes(0);
       auth.setFirstFailureTime(now);
       users.put(key,auth);  
       if(checkThread==null)new CheckTimeOut(Thread.currentThread());
      }
      else
      {
       if(auth.getFailureTimes()>maxFailureTimes)
       {
                //如果在限定的时间间隔内,则返回拒绝用户连接的信息
        if((now.getTime()-auth.getFirstFailureTime().getTime())<maxFailureInterval)
        {
         ret=false;
         auth.setStatus(-1);
        }
        else  if(auth.getStatus()==-1 && (now.getTime()-auth.getFirstFailureTime().getTime()<(maxFailureInterval+waitInterval)))//重置计数器
        {
         ret=false;
        }
        else   
        {   
         auth.setFailureTimes(0);
         auth.setFirstFailureTime(now);
         auth.setStatus(0);
        }
       
       }
       //登录次数加1
       auth.setFailureTimes(auth.getFailureTimes()+1);
      }
      //System.out.println(key+":"+auth.getFailureTimes()+":"+ret+":"+(now.getTime()-auth.getFirstFailureTime().getTime()));
      return ret;
    } public static void reset(String ip,String userName)//重置用户信息
    {
      Date now=new Date();
      String key=ip+":"+userName;
      UserConnect auth=(UserConnect)users.get(key);
      if(auth==null)//把用户当前的访问信息加入到users容器中
      {
       auth=new UserConnect();
       auth.setIp(ip);
       auth.setUserName(userName);
       auth.setFailureTimes(0);
       auth.setFirstFailureTime(now);
       users.put(key,auth);
      }
      else
      {
       auth.setFailureTimes(0);
       auth.setFirstFailureTime(now);
      }
    }
    public static void remove(String ip,String userName)//删除用户在容器中的记录
    {
      String key=ip+":"+userName;
      users.remove(key);
    }
    public static void clear()//清空容器中内容
    {
      if(!users.isEmpty())users.clear();
    }
    public static long getMaxFailureInterval() {
      return maxFailureInterval;
    } public static void setMaxFailureInterval(long maxFailureInterval) {
      UserConnectManage.maxFailureInterval = maxFailureInterval;
    } public static int getMaxFailureTimes() {
      return maxFailureTimes;
    } public static void setMaxFailureTimes(int maxFailureTimes) {
      UserConnectManage.maxFailureTimes = maxFailureTimes;
    } public static int getMaxOnlineUser() {
      return maxOnlineUser;
    } public static void setMaxOnlineUser(int maxOnlineUser) {
      UserConnectManage.maxOnlineUser = maxOnlineUser;
    } public static long getWaitInterval() {
      return waitInterval;
    } public static void setWaitInterval(long waitInterval) {
      UserConnectManage.waitInterval = waitInterval;
    }
    四、调用接口

    在需要进入侵检测判断的地方,直接使用UserConnectManage类中的checkLoginValidate方法即可。如EasyJWeb的核心Servlet

    com.easyjf.web.ActionServlet中调用UserConnectManage的代码:
        if(!UserConnectManage.checkLoginValidate(request.getRemoteAddr(),"guest"))
            {            
                info(request,response,new Exception("您对页面的刷新太快,请等待"+UserConnectManage.getWaitInterval()/1000+"秒

    后再刷新页面!"));
                return;
            }      



    五、总结
    当然,这里提供的方法只是一个简单的实现示例,由于上面的用户信息是直接保存在内存中,若并发用户很大的时候的代码的占用,可以考虑引入数据库来记录用户的访问信息,当然相应的执行效率肯定用降低。上面介绍的实现中,入侵检测判断的策略也只有用户访问次数及时间间隔两个元素,您还可以根据你的实现情况增加其它的检测元素。

    由于水平有限,该设计上有N不合理或者需要改进的地方,恳请大家指正
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|Java学习者论坛 ( 声明:本站资料整理自互联网,用于Java学习者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2024-5-3 17:16 , Processed in 0.423740 second(s), 52 queries .

    Powered by Discuz! X3.4

    © 2001-2017 Comsenz Inc.

    快速回复 返回顶部 返回列表