java web項目防止多用戶重復登錄解決方案


原創作品,允許轉載,轉載時請務必以超鏈接形式標明文章 原始出處 、作者信息和本人聲明。否則將追究法律責任。
作者:永恆の_☆    地址:http://blog.csdn.net/chenghui0317/article/details/9373345

     目前web項目中,很多情況都是可以讓同一個賬戶信息在不同的登錄入口登錄這次,這樣子就不那么美好了。

現在有兩種解決方案:

    1、將用戶的登錄信息用一個標志位的字段保存起來,每次登錄成功就標記1,注銷登錄就標記為0,當標記為1的時候不允許別人登錄。

    2、將用戶的登錄信息保存在application內置作用域內, 然后利用session監聽器監聽每一個登錄用戶的登錄情況。

很顯然,第一種方式 每次登錄 都需要操作數據庫,多了一些不必要的性能開銷,而且在登錄狀態下 萬一突然電腦關閉了,那就永遠都不能登錄了,可用性比較低。

但是第二種方式就不一樣了,可操作性強,很方便維護所有在線用戶的信息。

接下來 主要介紹第二種方式的具體實現:

    1、在處理登錄的login方法中,先查詢數據庫驗證下該用戶是否存在,如果存在 判斷該登錄賬戶是否已經鎖定了, 然后從application內置作用域對象中取出所有的登錄信息,查看該username賬戶是否已經登錄,如果登錄了,就友好提示下,反之表示可以登錄,將該登錄信息以鍵值對的方式保存在application中。

代碼如下:

 

[java]  view plain copy print ?
 
  1. //沒有使用零配置前 每個訪問的方法都要加上@Action ,否則404  
  2. @Action(value="login", results={  
  3.         @Result(name="index", location="index.jsp"),  
  4. })  
  5. public String login() throws Exception {  
  6.     try{  
  7.         User result = userService.login(user.getFuUserName(), user.getFuPassword());  
  8.         if(result!=null){  
  9.             if(result.getFuStatus()!=null && result.getFuStatus()==0){  
  10.                 super.setRequestAttr(Constant.MESSAGE, "抱歉,該用戶已被鎖定!");  
  11.                 return "error";  
  12.             }  
  13.             Map<String, String> loginUserMap = (Map<String, String>) super.getApplicationAttr(Constant.LOGIN_USER_MAP);  
  14.             boolean isExist = false;  
  15.             String sessionId = super.getSessionId(false);  
  16.             if(loginUserMap==null){  
  17.                 loginUserMap = new HashMap<String, String>();  
  18.             }  
  19.             for (String username : loginUserMap.keySet()) {  
  20.                 //判斷是否已經保存該登錄用戶的信息         或者     如果是同一個用戶進行重復登錄那么允許登錄  
  21.                 if(!username.equals(result.getFuUserName()) || loginUserMap.containsValue(sessionId)){  
  22.                     continue;  
  23.                 }  
  24.                 isExist = true;  
  25.                 break;  
  26.             }                 
  27.             if(isExist){  
  28.                 super.setRequestAttr(Constant.MESSAGE, "抱歉,該用戶已登錄!");  
  29.                 return "error";  
  30.             }else {  
  31.                 loginUserMap.put(result.getFuUserName(), sessionId);  
  32.             }  
  33.             //登錄成功  
  34.             super.setSessionAttr(Constant.LOGIN_USER, result);  
  35.             super.setApplicationAttr(Constant.LOGIN_USER_MAP, loginUserMap);  
  36.               
  37.             logger.info(result.getFuUserName() + " 登錄成功!");  
  38.             //如果 session中fromUrl有值,就跳轉到該頁面  
  39.             String fromUrl = (String)super.getSessionAttr(Constant.FROM_URL);  
  40.             if(fromUrl!=null){  
  41.                 super.setSessionAttr(Constant.FROM_URL, null);  
  42.                 super.getResponse().sendRedirect(fromUrl.toString());  
  43.                 return null;  
  44.             }  
  45.             return "index";  
  46.         }  
  47.     }  
  48.     catch (Exception e) {  
  49.         e.printStackTrace();  
  50.         logger.info("登錄失敗: "+e.getMessage());  
  51.     }  
  52.     super.setRequestAttr("message", "用戶名或密碼錯誤");  
  53.     return "error";  
  54. }  


    2、登錄入口處理完之后,考慮到會話結束的話,那么對應的登錄用戶也應該相應的注銷登錄。我們可以寫一個Session監聽器,監聽sessioon銷毀的時候,我們將登錄的用戶注銷掉,也就是從application中移除。表示該用戶已經下線了。

 

代碼如下:

 

[java]  view plain copy print ?
 
  1. package com.facelook.util;  
  2.   
  3. import java.util.Map;  
  4.   
  5. import javax.servlet.http.HttpSessionEvent;  
  6. import javax.servlet.http.HttpSessionListener;  
  7.   
  8. import org.apache.log4j.Logger;  
  9.   
  10. import com.facelook.entity.User;  
  11.   
  12. public class SessionListener implements HttpSessionListener{  
  13.   
  14.     private Logger logger = Logger.getLogger(this.getClass());  
  15.       
  16.     @Override  
  17.     public void sessionCreated(HttpSessionEvent event) {  
  18.           
  19.     }  
  20.   
  21.     @Override  
  22.     public void sessionDestroyed(HttpSessionEvent event) {  
  23.         //在session銷毀的時候 把loginUserMap中保存的鍵值對清除  
  24.         User user = (User)event.getSession().getAttribute("loginUser");  
  25.         if(user!=null){  
  26.             Map<String, String> loginUserMap = (Map<String, String>)event.getSession().getServletContext().getAttribute("loginUserMap");  
  27.             loginUserMap.remove(user.getFuUserName());  
  28.             event.getSession().getServletContext().setAttribute("loginUserMap",loginUserMap);  
  29.         }  
  30.           
  31.     }  
  32.   
  33. }  

web.xml中配置如下:

 

 

[html]  view plain copy print ?
 
  1. <!-- session listener -->  
  2. <listener>  
  3.     <listener-class>com.facelook.util.SessionListener</listener-class>  
  4. </listener>  


    3、另外,還有一個問題,如果說登錄的用戶突然關閉了瀏覽器或者頁面而沒有點擊退出按鈕。那么可以利用beforeunload 事件,在瀏覽器刷新或者關閉的時候觸發。

 

 

[java]  view plain copy print ?
 
  1. //在刷新或關閉時調用的事件  
  2. $(window).bind('beforeunload',function(){  
  3.   $.ajax({  
  4.     url:"${ctx}/system/user/user!logout.action",  
  5.     type:"post",  
  6.     success:function(){  
  7.         alert("您已退出登錄");  
  8.     }  
  9. });  
  10. );  

但是如果一些客觀原因,比如電腦突然關機,自動重啟,等等,這些就沒法避免了,所以只能等待服務器端的session會話重置之后才可以再登錄。

除非 做一個 統計所有在線人員的模塊,管理員在里面進行在線人員的登錄登出的狀態管理,把那些有問題的登錄用戶直接銷毀掉。

 

接下來簡單介紹下在線人員模塊的管理:

   1、首先需要一個session監聽器來監聽所有的回話create的情況,這時候每次創建一個session就可以count+1 ,然后銷毀的時候count-1 ,另外還需要一個ServletContext的監聽器來監聽web應用的生命周期,獲取servletContext對象,然后將在線人員總數統計出來存放進去;

具體代碼如下:

 

[java]  view plain copy print ?
 
  1. package com.facelook.util;  
  2.   
  3. import java.util.Map;  
  4.   
  5. import javax.servlet.ServletContext;  
  6. import javax.servlet.ServletContextEvent;  
  7. import javax.servlet.ServletContextListener;  
  8. import javax.servlet.http.HttpSessionEvent;  
  9. import javax.servlet.http.HttpSessionListener;  
  10.   
  11. import org.apache.log4j.Logger;  
  12.   
  13. import com.facelook.entity.User;  
  14.   
  15. public class SessionListener implements HttpSessionListener,ServletContextListener{  
  16.   
  17.     private int count;  
  18.     private ServletContext servletContext = null;  
  19.       
  20.     public SessionListener() {  
  21.         count = 0;  
  22.     }  
  23.   
  24.     private Logger logger = Logger.getLogger(this.getClass());  
  25.     @Override  
  26.     public void sessionCreated(HttpSessionEvent event) {  
  27.         count++;  
  28.         setContext(event);  
  29.         logger.info("***************the  http session is created...***************");  
  30.     }  
  31.   
  32.     @Override  
  33.     public void sessionDestroyed(HttpSessionEvent event) {  
  34.         //在session銷毀的時候 把loginUserMap中保存的鍵值對清除  
  35.         User user = (User)event.getSession().getAttribute("loginUser");  
  36.         if(user!=null){  
  37.             Map<String, String> loginUserMap = (Map<String, String>)event.getSession().getServletContext().getAttribute("loginUserMap");  
  38.             loginUserMap.remove(user.getFuUserName());  
  39.             event.getSession().getServletContext().setAttribute("loginUserMap",loginUserMap);  
  40.         }  
  41.           
  42.         count--;  
  43.         setContext(event);  
  44.         logger.info("***************the  http session is destroyed...***************");  
  45.     }  
  46.   
  47.     public void setContext(HttpSessionEvent httpSessionEvent){  
  48.         httpSessionEvent.getSession().getServletContext().setAttribute("online", count);  
  49.     }  
  50.       
  51.       
  52.     @Override  
  53.     public void contextDestroyed(ServletContextEvent servletcontextevent) {       
  54.         this.servletContext = null;  
  55.         logger.info("***************the  servlet context is destroyed...***************");  
  56.     }  
  57.   
  58.     @Override  
  59.     public void contextInitialized(ServletContextEvent servletcontextevent) {  
  60.         this.servletContext = servletcontextevent.getServletContext();  
  61.         logger.info("***************the  servlet context is initialized...***************");  
  62.     }  
  63.   
  64. }  

 

   2、在UserAction中創建管理在線用戶的模塊的方法,並且支持強制退出的功能;

 

[java]  view plain copy print ?
 
  1. /** 
  2.  * 退出登錄 
  3.  * @return 
  4.  * @throws ServletException 
  5.  * @throws IOException 
  6.  */  
  7. public String logout() throws ServletException, IOException{  
  8.     try {  
  9.         Map<String, String> loginUserMap = (Map<String, String>) super.getApplicationAttr(Constant.LOGIN_USER_MAP);  
  10.         User user = (User) super.getSessionAttr(Constant.LOGIN_USER);  
  11.         super.removeAttribute(Constant.LOGIN_USER_MAP);  
  12.         loginUserMap.remove(user.getFuUserName());  
  13.         super.setApplicationAttr(Constant.LOGIN_USER_MAP,loginUserMap);  
  14.         logger.info("退出登錄成功!");  
  15.     } catch (Exception e) {  
  16.         e.printStackTrace();  
  17.         logger.error("退出登錄失敗: "+e.getMessage());  
  18.     }  
  19.     return INPUT;  
  20. }  
  21.   
  22. /** 
  23.  * 在線用戶管理 
  24.  * @return 
  25.  */  
  26. public String loginManager(){  
  27.     return SUCCESS;  
  28. }  
  29.   
  30. /** 
  31.  * 強制退出其他用戶 
  32.  * @return 
  33.  */  
  34. public String logoutOther(){  
  35.     try {  
  36.         String username = ServletActionContext.getRequest().getParameter("username");  
  37.         Map<String, String> loginUserMap = (Map<String, String>) super.getApplicationAttr(Constant.LOGIN_USER_MAP);  
  38.           
  39.         if(username!=null && loginUserMap.containsKey(username)){  
  40.             loginUserMap.remove(username);  
  41.             super.setApplicationAttr(Constant.LOGIN_USER_MAP, loginUserMap);              
  42.         }  
  43.     } catch (Exception e) {  
  44.         e.printStackTrace();  
  45.         logger.info("強制退出失敗: "+e.getMessage());  
  46.     }  
  47.     return null;  
  48. }  

 

   3、在管理頁面加載在線用戶的列表;

對應的方法定義完畢之后,然后再在對應的管理頁面添加在線列表,具體如下:

 

[html]  view plain copy print ?
 
  1. <%@page import="java.util.Map"%>  
  2. <%@page import="java.util.Map.Entry"%>  
  3. <%@ page language="java" pageEncoding="UTF-8" %>  
  4. <%@ include file="/common/taglib.jsp" %>  
  5. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">  
  6. <html xmlns="http://www.w3.org/1999/xhtml">  
  7. <head>  
  8. <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />  
  9. <title>歡迎來到Facelook</title>  
  10. <%@ include file="/common/resource.jsp" %>  
  11. <script type="text/javascript">  
  12.   <!--  
  13.   //在刷新或關閉時調用的事件  
  14.   $(window).bind('beforeunload',function(){  
  15.        $.ajax({  
  16.             url:"${ctx}/system/user/user!logout.action",  
  17.             type:"post",  
  18.             success:function(){  
  19.                 alert("您已退出登錄");  
  20.             }  
  21.         });  
  22.    });  
  23.     
  24.   function logout(username){  
  25.         if(username=="${sessionScope.loginUser.fuUserName}"){  
  26.             alert("不允許退出自己賬號!");  
  27.             return;  
  28.         }  
  29.         $.ajax({  
  30.             url:"${ctx}/system/user/user!logoutOther.action?username="+username,  
  31.             type:"post",  
  32.             success:function(){  
  33.                 $("#tr"+username).hide();  
  34.                 var count = parseInt($("#count").html());  
  35.                 $("#count").html(count-1);  
  36.                 alert("退出成功!");  
  37.             }  
  38.         });  
  39.     }  
  40.   //-->  
  41. </script>  
  42. </head>  
  43. <body>  
  44. <%@ include file="/common/header.jsp" %>  
  45. <div id="main" class="wrap">  
  46.     <%@ include file="/common/lefter.jsp" %>  
  47.     <div class="righter">  
  48.         <div class="main">  
  49.             <h2>登錄列表</h2>  
  50.             <%  
  51.             Map<String,Stringmap = (Map<String,String>)application.getAttribute("loginUserMap");  
  52.             out.println("目前共有<font id='count'>"+map.size()+"</font>個用戶在線!!");  
  53.             %>  
  54.             <table border="1" width="400">  
  55.             <%for(Entry<String,String> m : map.entrySet()){%>  
  56.                 <tr id="tr<%=m.getKey()%>">  
  57.                     <td>  
  58.                         <%=m.getKey()%>  
  59.                     </td>  
  60.                     <td width="80">  
  61.                         <href="javascript:logout('<%=m.getKey()%>')">強制退出</a>  
  62.                     </td>  
  63.                 </tr>      
  64.             <%}%>  
  65.             </table>  
  66.         </div>  
  67.     </div>  
  68. </div>   
  69. <%@ include file="/common/footer.jsp" %>  
  70. <%@ include file="/common/message.jsp" %>                      
  71. </body>  
  72. </html>  


好了啟動部署項目,然后啟動服務,進入在線用戶管理模塊,簡單效果如下圖:

 

需要注意的是:當前登錄用戶 不允許強制退出自己的登錄信息。

 

這樣子,基本上可以實現防止多用戶登錄的案例了!

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM