java web實現同一賬號在不同瀏覽器不能同時登入
前提都是用session監聽器,對session的建立與銷燬進行監聽
一、在使用者登入時儲存該使用者的狀態有這幾種儲存方式:
1、儲存到記憶體中(application ,servletcontext ,個人也是推薦這種方式)
2、儲存狀態到資料庫,至於具體的怎麼儲存你可以隨意返回,如:0,1兩種狀態
3、儲存到檔案中,對檔案的讀寫
以上是登入使用者的狀態,這對於大家來說都沒有什麼問題。
二、 這時如果同一使用者登入了,你可以在登入成功後(儲存資訊之前)進入到上面的狀態 中進行匹配判斷,如果存在就提醒此使用者你的賬號已登入。
三、關鍵在於該使用者的銷燬
invliate(),呼叫這個方法,通過Session的監聽器,進行對當前使用者的刪除。
Session過期,也會呼叫Session監聽器。
討論最多的就是在比如使用者沒有自己去登出session.invalidate()方法,而是直接關閉了 瀏覽器,這時怎麼辦?(瀏覽器關閉半個小時候,預設登出session,監聽器這時才呼叫sessionDestroyed方法)。
首先要明確的一點,使用者關閉瀏覽器,伺服器端是無法得知的(因為web客戶瀏覽器與伺服器之間是無狀態的),網上也有一些解決方案,比如通過js來判斷使用者是否關閉了瀏覽器,是的次方法可行,但是如果使用者操作時,打開了多個視窗呢,這裡也有方法:
1、一個方法還是比較好的,通過cokkie儲存當前使用者開了的頁面數,如果頁數為一的話,就可以觸發js的登出Session了。
2、先把Session的生命期設定的短一點,用類似ajax這種非同步請求方式週期不斷的去請求後臺,這樣可以保證當前的這個Session有效,如果使用者退出後,該Session也會很快的過期。
上面的兩種方式都有些缺陷,如:
1、使用者刪除cokkie的話,就無法判斷準確,這種可能應該不多吧(在訪問當前網站時)
2、有延遲,伺服器壓力相對來說比較大
還有一種方式與上面的思考角度不一樣(當然也要看需求了),如果有使用者登入時,就登出之前已登入的同一使用者。
實現方式與上面應該是差不多的,唯一不同之處在於,不用去刻意的想辦法讓使用者退出了(登出Session)。
四、雖然像百度,CSDN等這些網站都沒有這樣做,當然也沒有必要這要這樣做。
但是在我們做企業內部應用的時候有時候可能會要求我們這樣做(同一賬號在統一時間只能在一個登入),下面是個例子
我們先定義兩個Map物件需要用到,存放使用者與HttpSession的關係和sessionId與使用者的關係。用來記錄當前登入的使用者是否登入和當前session是否已經綁定了登入使用者。
- /**
- * 使用者和Session繫結關係
- */
- publicstaticfinal Map<String, HttpSession> USER_SESSION=new HashMap<String, HttpSession>();
- /**
- * seeionId和使用者的繫結關係
- */
- publicstaticfinal Map<String, String> SESSIONID_USER=new HashMap<String, String>();
- publicvoid sessionDestroyed(HttpSessionEvent se) {
- String sessionId=se.getSession().getId();
- //當前session銷燬時刪除當前session繫結的使用者資訊
- //同時刪除當前session繫結使用者的HttpSession
- USER_SESSION.remove(SESSIONID_USER.remove(sessionId));
- }
接下來是關鍵,處理登入使用者的唯一。
當用戶登入進來的時候我們先把當前HttpSession繫結的使用者和使用者繫結的HttpSession關係刪除。
再次我們刪除當前登入的使用者繫結的HttpSession關係,如果當前使用者已經登入刪除session和使用者的關係、刪除session中使用者的資訊、設定說明使用者被擠下線了。
- /**
- * 使用者登入時的處理
- * 處理一個賬號同時只有一個地方登入的關鍵
- * @param request
- */
- publicstaticvoid userLoginHandle(HttpServletRequest request){
- //當前登入的使用者
- String userName=request.getParameter("userName");
- //當前sessionId
- String sessionId=request.getSession().getId();
- //刪除當前sessionId繫結的使用者,使用者--HttpSession
- USER_SESSION.remove(SESSIONID_USER.remove(sessionId));
- //刪除當前登入使用者繫結的HttpSession
- HttpSession session=USER_SESSION.remove(userName);
- if(session!=null){
- SESSIONID_USER.remove(session.getId());
- session.removeAttribute("userName");
- session.setAttribute("userMsg", "您的賬號已經在另一處登入了,你被迫下線!");
- }
- }
使用者登入的請求處理。先判斷使用者輸入的登入資訊是否合法,在判斷使用者輸入的資訊是否正確(登入是否成功),成功則呼叫單使用者登入的處理方法,把當前登入的使用者和當前session關聯,session中保持當前登入使用者的資訊
- /**
- * 使用者登入
- */
- protectedvoid service(HttpServletRequest request,
- HttpServletResponse response) throws ServletException, IOException {
- String userName=request.getParameter("userName");
- String password=request.getParameter("password");
- if(userName!=null&&!"".equals(userName.trim())){
- //登入成功
- if(login(userName, password)){
- HttpSession session=request.getSession();
- //處理使用者登入(保持同一時間同一賬號只能在一處登入)
- userLoginHandle(request);
- //新增使用者與HttpSession的繫結
- USER_SESSION.put(userName.trim(), session);
- //新增sessionId和使用者的繫結
- SESSIONID_USER.put(session.getId(), userName);
- session.setAttribute("userName", userName);
- session.removeAttribute("userMsg");
- }
- }
- response.sendRedirect("index.jsp");
- }
- //對比使用者輸入的資訊是否合法,從而判斷是否登入成功
- privateboolean login(String userName,String password){
- //////////////
- returntrue;
- }
對於,同一時間同一賬戶可以在兩處乃至三處登入的處理類似上面的處理流程,可以建個佇列來實現先進先出。