当用户登录后,服务器要如何记录下用户的在线状态,这样别人想用这个帐号再次登录时就可以先检测到该帐号已经在线,不能再登录了;当已经登录的帐号退出时,如何立即取消用户的在线状态呢?

解决方案 »

  1.   

    用session来实现用户工作区
    登录则创建,推出则销毁!
      

  2.   

    销毁用户.
    session.removeAttribute("user");
      

  3.   

    利用session或者cookie
    登錄時用session獲取用戶名
    HttpSession session=req.getSession();
    session.setAttribute("user",username); 
    退出時清空session
    HttpSession session=req.getSession();
    session.invalidate();
    程序運行時判斷該session不為空,即認為有人登錄。
    在xml中可以設置保存session保存時間
    <session-config>
            <session-timeout>
                30
            </session-timeout>
    </session-config>
      

  4.   

    《JSP环境基于Session的在线用户统计深入分析,监听器Listener实现用户在线统计》我以前转过一篇文章:http://heisetoufa.ggblog.com/332131.htmlJSP环境基于Session的在线用户统计深入分析JSP作为后起之秀能够在服务器编程环境中占据一定地位,是和它良好支持一系列业界标准
    密切相关的。Session就是它提供的基础设施之一。作为一个程序员,你可以不介意具体在
    客户端是如何实现,就方便的实现简单的基于session的用户管理。现在对于处理在线用户,有几种不同的处理方法。一种是叶面刷新由用户控制,服务器端控制一个超时时间比如30分钟,到了时间之后用户
    没有动作就被踢出。这种方法的优点是,如果用户忘了退出,可以防止别人恶意操作。
    缺点是,如果你在做一件很耗时间的事情,超过了这个时间限制,submit的时候可能要
    再次面临登陆。如果原来的叶面又是强制失效的话,就有可能丢失你做的工作。在实现
    的角度来看,这是最简单的,Server端默认实现的就是这样的模式。另一种方式是,站点采用框架结构,有一个Frame或者隐藏的iframe在不断刷新,这样你
    永远不会被踢出,但是服务器端为了判断你是否在线,需要定一个发呆时间,如果超过
    这个发呆时间你除了这个自动刷新的叶面外没有刷新其他叶面的话,就认为你已经不在
    线了。采取这种方式的典型是xici.net。 他的优点是可以可以利用不断的刷新实现一些
    类似server-push的功能,比如网友之间发送消息。不管哪一种模式,为了实现浏览当前所有的在线用户,还需要做一些额外的工作。
    servlet API中没有得到Session列表的API。可以利用的是Listener. Servlet 2.2和2.3规范在这里略微有一些不一样。
    2.2中HttpSessionBindingListener可以实现当一个HTTPSession中的Attribute变化的
    时候通知你的类。而2.3中还引入了HttpSessionAttributeListener.鉴于我使用的环境
    是Visual age for java 4和JRun server 3.1,他们还不直接支持Servlet 2.3的编程,
    这里我用的是HttpSessionBindingListener.需要做的事情包括做一个新的类来实现HttpSessionBindingListener接口。这个接口有
    两个方法:
    public void valueBound(HttpSessionBindingEvent event),和
    public void valueUnbound(HttpSessionBindingEvent event)。
    当你执行Session.addAttribute(String,Object)的时候,如果你已经把一个实现了
    HttpSessionBindingListener接口的类加入为Attribute,Session会通知你的类,调用
    你的valueBound方法。相反,Session.removeAttribute方法对应的是valueUndound方法。 public class HttpSessionBinding implements javax.servlet.http.HttpSessionBindingListener 
    {
     ServletContext application = null; public HttpSessionBinding(ServletContext application)
     {
      super();
      if (application ==null)
       throw new IllegalArgumentException("Null application is not accept.");
      
      this.application = application;
      
     } public void valueBound(javax.servlet.http.HttpSessionBindingEvent e) 
     { 
      Vector activeSessions = (Vector) application.getAttribute("activeSessions");
      if (activeSessions == null)
      {
       activeSessions = new Vector();
      }  JDBCUser sessionUser = (JDBCUser)e.getSession().getAttribute("user");
      if (sessionUser != null)
      {
       activeSessions.add(e.getSession());
      }
      application.setAttribute("activeSessions",activeSessions);
     } public void valueUnbound(javax.servlet.http.HttpSessionBindingEvent e) 
     {
      JDBCUser sessionUser = (JDBCUser)e.getSession().getAttribute("user");
      if (sessionUser == null)
      {
       Vector activeSessions = (Vector) application.getAttribute("activeSessions");
       if (activeSessions != null)
       {
        activeSessions.remove(e.getSession().getId());
        application.setAttribute("activeSessions",activeSessions);
       }
      }
     }
    }
    假设其中的JDBCUser类是一个任意User类。
    在执行用户登录时,把User类和HttpSessionBinding类都加入到Session中去。
    这样,每次用户登录后,在application中的attribute "activeSessions"这个vector中
    都会增加一条记录。
    每当session超时,valueUnbound被触发,在这个vector中删去将要被超时的session.
    public void login()
    throws ACLException,SQLException,IOException
    {
     /* get JDBC User Class */
     if (user != null)
     {
      logout();
     }
     {
      // if session time out, or user didn't login, save the target url temporary.  JDBCUserFactory uf = new JDBCUserFactory();  if ( (this.request.getParameter("userID")==null) 
       || (this.request.getParameter("password")==null)  )
      {
       throw new ACLException("Please input a valid userName and password.");
      }
      
      JDBCUser user = 
       (JDBCUser) uf.UserLogin(
        this.request.getParameter("userID"),
        this.request.getParameter("password") );
      user.touchLoginTime();
      this.session.setAttribute("user",user);
      this.session.setAttribute("BindingNotify",new HttpSessionBinding(application));
     }
    }
    Login的时候,把User和这个BindingNotofy目的的类都加入到session中去。logout的时候,就要主动在activeSessions这个vector中删去这个session.public void logout()
    throws SQLException,ACLException
    {
     if (this.user == null 
      && this.session.getAttribute("user")==null)
     {
      return;
     } Vector activeSessions = (Vector) this.application.getAttribute("activeSessions");
     if (activeSessions != null)
     {
      activeSessions.remove(this.session);
      application.setAttribute("activeSessions",activeSessions);
     } java.util.Enumeration e = this.session.getAttributeNames(); while (e.hasMoreElements())
     {
      String s = (String)e.nextElement();
      this.session.removeAttribute(s);
     }
     this.user.touchLogoutTime();
     this.user = null;
    }
    这两个函数位于一个HttpSessionManager类中.这个类引用了jsp里面的application全局
    对象。
    这个类的其他代码和本文无关且相当长,我就不贴出来了。下面来看看jsp里面怎么用。
    假设一个登录用的表单被提交到doLogin.jsp, 表单中包含UserName和password域。节选部分片段:<%
     HttpSessionManager hsm = new HttpSessionManager(application,request,response);
     try
     {
      hsm.login();
     }
     catch ( UserNotFoundException e)
     {
      response.sendRedirect("InsufficientPrivilege.jsp?detail=User%20does%20not%20exist.");
      return;
     }
     catch ( InvalidPasswordException e2)
     { 
      response.sendRedirect("InsufficientPrivilege.jsp?detail=Invalid%20Password");
      return;
     }
     catch ( Exception e3)
     {
     %> Error:<%=e3.toString() %><br>
      Press <a href="http://www.ygblog.com/login.jsp">Here</a> to relogin.
    <%  return;
     }
     response.sendRedirect("index.jsp");
    %>
    再来看看现在我们怎么得到一个当前在线的用户列表。<body bgcolor="#FFFFFF">
    <table cellspacing="0" cellpadding="0" width="100%"> <tr >
       <td style="width:24px">SessionId
       </td>
       <td style="width:80px" >User
       </td>
       <td style="width:80px" >Login Time
       </td>
       <td style="width:80px" >Last access  Time </td> </tr> <% Vector activeSessions = (Vector) application.getAttribute("activeSessions"); if (activeSessions == null) { activeSessions = new Vector(); application.setAttribute("activeSessions",activeSessions); } Iterator it = activeSessions.iterator(); while (it.hasNext()) { HttpSession sess = (HttpSession)it.next(); JDBCUser sessionUser = (JDBCUser)sess.getAttribute("user"); String userId = (sessionUser!=null)?sessionUser.getUserID():"None"; %> <tr> <td nowrap=''><%= sess.getId() %></td> <td nowrap=''><%= userId %></td> <td nowrap=''> <%= BeaconDate.getInstance( new java.util.Date(sess.getCreationTime())).getDateTimeString()%></td> <td class="<%= stl %>3" nowrap=''> <%= BeaconDate.getInstance( new java.util.Date(sess.getLastAccessedTime())).getDateTimeString()%></td> </tr> <% } %> </table> </body> 以上的代码从application中取出activeSessions,并且显示出具体的时间。其中
    BeaconDate类假设为格式化时间的类。这样,我们得到了一个察看在线用户的列表的框架。至于在线用户列表分页等功能,
    与本文无关,不予讨论。
    这是一个非刷新模型的例子,依赖于session的超时机制。我的同事sonymusic指出很
    多时候由于各个厂商思想的不同,这有可能是不可信赖的。考虑到这种需求,需要在
    每个叶面刷新的时候都判断当前用户距离上次使用的时间是否超过某一个预定时间值。
    这实质上就是自己实现session超时。
    如果需要实现刷新模型,就必须使用这种每个叶面进行刷新判断的方法。
      

  5.   

    还有一个
    《监听器配置,java监听器,登陆监听,Listener,struts监听器配置》http://heisetoufa.ggblog.com/329724.html-----------------------------------------------------
    首先感谢老紫竹群中dgqbcht的帮助,教会了我监听器的配置切入正题,先说一下什么是监听器,监听器也叫Listener,是Servlet的监听器,它可以监听客户端的请求、服务端的操作等。通过监听器,可以自动激发一些操作,比如监听在线的用户的数量。当增加一个HttpSession时,就激发sessionCreated(HttpSessionEvent   se)方法,这样就可以给在线人数加1。常用的监听接口有以下几个:ServletContextAttributeListener监听对ServletContext属性的操作,比如增加、删除、修改属性。   ServletContextListener监听ServletContext。当创建ServletContext时,激发contextInitialized(ServletContextEvent   sce)方法;当销毁ServletContext时,激发contextDestroyed(ServletContextEvent   sce)方法。   HttpSessionListener监听HttpSession的操作。当创建一个Session时,激发session   Created(HttpSessionEvent   se)方法;当销毁一个Session时,激发sessionDestroyed   (HttpSessionEvent   se)方法。   HttpSessionAttributeListener监听HttpSession中的属性的操作。当在Session增加一个属性时,激发attributeAdded(HttpSessionBindingEvent   se)   方法;当在Session删除一个属性时,激发attributeRemoved(HttpSessionBindingEvent   se)方法;当在Session属性被重新设置时,激发attributeReplaced(HttpSessionBindingEvent   se)   方法。  下面帖上一个简单的配置<listener>
          <listener-class>listener.MySessionListener</listener-class>
      </listener>把它放到<web-app>里再建一个监听类package listener;import javax.servlet.http.HttpSessionEvent;
    import javax.servlet.http.HttpSessionListener;public class MySessionListener implements HttpSessionListener {
     public void sessionCreated(HttpSessionEvent se) {
      // 当session建立时触发
      System.out.println("当session建立时触发");
     } public void sessionDestroyed(HttpSessionEvent se) {
      // 当session销毁时触发
      System.out.println("当session销毁时触发");
     }
    }这样就配置成功了,打开页面时就会输出当session建立时触发,退出时输出当session销毁时触发!下面我们开发一个具体的例子,这个监听器能够统计在线的人数。在ServletContext初始化和销毁时,在服务器控制台打印对应的信息。当ServletContext里的属性增加、改变、删除时,在服务器控制台打印对应的信息。  
              要获得以上的功能,监听器必须实现以下3个接口:   
                  
      HttpSessionListener   
                  
      ServletContextListener   
                  
      ServletContextAttributeListener     
        
              我们看具体的代码,见示例14-9。     
        
              【程序源代码】     
        
      1 //   ====================   Program   Discription   =====================   
      2 //   程序名称:示例14-9   :   EncodingFilter   .java   
      3 //   程序目的:学习使用监听器   
      4 //   ==============================================================   
      5 import   javax.servlet.http.*;   
      6 import   javax.servlet.*;   
      7   
      8 public   class   OnLineCountListener   implements   HttpSessionListener,   
      ServletContextListener,ServletContextAttributeListener   
      9 {   
      10 private   int   count;   
      11 private   ServletContext   context   =   null;   
      12   
      13 public   OnLineCountListener()   
      14 {   
      15 count=0;   
      16 //setContext();   
      17 }   
      18 //创建一个session时激发   
      19 public   void   sessionCreated(HttpSessionEvent   se)     
      20 {   
      21 count++;   
      22 setContext(se);   
      23   
      24 }   
      25 //当一个session失效时激发   
      26 public   void   sessionDestroyed(HttpSessionEvent   se)     
      27 {   
      28 count--;   
      29 setContext(se);   
      30 }   
      31 //设置context的属性,它将激发attributeReplaced或attributeAdded方法   
      32 public   void   setContext(HttpSessionEvent   se)   
      33 {   
      34 se.getSession().getServletContext().   
      setAttribute("onLine",new   Integer(count));   
      35 }   
      36   //增加一个新的属性时激发   
      37 public   void   attributeAdded(ServletContextAttributeEvent   event)   {   
      38   
      39 log("attributeAdded('"   +   event.getName()   +   "',   '"   +   
      40         event.getValue()   +   "')");   
      41   
      42         }   
      43           
      44       //删除一个新的属性时激发   
      45         public   void   attributeRemoved(ServletContextAttributeEvent   event)   {   
      46   
      47 log("attributeRemoved('"   +   event.getName()   +   "',   '"   +   
      48         event.getValue()   +   "')");   
      49   
      50         }   
      51   
      52 //属性被替代时激发   
      53         public   void   attributeReplaced(ServletContextAttributeEvent   event)   {   
      54   
      55 log("attributeReplaced('"   +   event.getName()   +   "',   '"   +   
      56         event.getValue()   +   "')");   
      57         }   
      58         //context删除时激发   
      59           public   void   contextDestroyed(ServletContextEvent   event)   {   
      60   
      61 log("contextDestroyed()");   
      62 this.context   =   null;   
      63   
      64         }   
      65   
      66         //context初始化时激发   
      67         public   void   contextInitialized(ServletContextEvent   event)   {   
      68   
      69 this.context   =   event.getServletContext();   
      70 log("contextInitialized()");   
      71   
      72         }   
      73         private   void   log(String   message)   {   
      74   
      75         System.out.println("ContextListener:   "   +   message);   
      76         }         
      77 }   
        
              【程序注解】   
              在OnLineCountListener里,用count代表当前在线的人数,OnLineCountListener将在Web服务器启动时自动执行。当OnLineCountListener构造好后,把count设置为0。每增加一个Session,OnLineCountListener会自动调用sessionCreated(HttpSessionEvent   se)方法;每销毁一个Session,OnLineCountListener会自动调用sessionDestroyed(HttpSessionEvent   se)方法。当调用sessionCreated(HttpSessionEvent   se)方法时,说明又有一个客户在请求,此时使在线的人数(count)加1,并且把count写到ServletContext中。ServletContext的信息是所有客户端共享的,这样,每个客户端都可以读取到当前在线的人数。     
        
      为了使监听器生效,需要在web.xml里进行配置,如下所示:     
        
      <listener>   
                      <listener-class>OnLineCountListener</listener-class>   
              </listener>   
        
      测试程序:     
        
      <%@   page   contentType="text/html;charset=gb2312"   %>   
        
      目前在线人数:     
        
      <font   color=red><%=getServletContext().getAttribute("onLine")%></font><br>   
        
      退出会话:     
        
      <form   action="exit.jsp"   method=post>   
      <input   type=submit   value="exit">   
      </form>   
        
      getServletContext().getAttribute("onLine")获得了count的具体值。客户端调用     
        
      <%session.invalidate()   ;%>   
        
              使Session失效,这样监听器就会使count减1。     
        
              【运行程序】   
              web.xml做好以上的配置,把OnLineCountListener放在WEB-INF/class目录下,启动Web服务器,在浏览器里输入以下URL(根据具体情况不同):http://127.0.0.1:8080/ch14/listener.jsp     
        
              浏览器将会打印目前在线人数。在服务器端有以下输出:     
        
      …   
      ContextListener:   contextInitialized()   
      ContextListener:   attributeReplaced('org.apache.   
      catalina.WELCOME_FILES',   '[Ljava.lang.String;@1d98a')   
      …   
      ContextListener:   attributeAdded('onLine',   '1')   
      ContextListener:   attributeReplaced('onLine',   '1')   
      ContextListener:   attributeReplaced('onLine',   '0')   
      ContextListener:   attributeReplaced('onLine',   '1')   
      ContextListener:   attributeReplaced('onLine',   '2')
      

  6.   

    登陆:
    在用户表中设一个登陆标识位字段,当用户登陆时将此字段设置为以登陆。用户登陆时,首先检查此字段。
    退出:
    可以像上面说的设置session过期时间,也可以用js判断页面关闭时做一些移除session的操作,并更改用户的登陆标识位。