1. 程式人生 > >如何做到同一個賬號同一時段只能登入一個

如何做到同一個賬號同一時段只能登入一個

 在許多web專案中,需要禁止使用者重複登入。一般來說有兩種做法:

         一是在使用者表中維護一個欄位isOnLine(是否線上),使用者登入時,設定值為true,使用者退出時設定為false,在重複登入時,檢索到該欄位為true時,禁止使用者登入。這種方法有明顯的漏洞,及使用者在非正常情況退出(關閉瀏覽器、關機等)是,該欄位值一直為true,會導致使用者無法登入。

          而另一種比較通用的做法是使用session監聽,重複登入後,強制之前登入的session過期,從而踢出了該使用者。具體做法是:使用監聽器維護伺服器上快取的sessionMap,該map是以<session.getId(),session>的鍵值對,在登入後,使用userid替換session.getId(),從而使得sessionMap中維護的是<userid, session>的鍵值對。後續該帳號重複登入時,檢索到已有該帳號session則強制它過期。

1、web.xml中配置session監聽

  1. <listener>
  2.         <listener-class>com.cnpc.framework.listener.SessionListener</listener-class>
  3. </listener>

2、session監聽SessionListener類
  1. package com.cnpc.framework.listener;  
  2. import javax.servlet.http.HttpSessionEvent;  
  3. import javax.servlet.http.HttpSessionListener;  
  4. import com.cnpc.framework.utils.SessionContext;  
  5. public class SessionListener implements HttpSessionListener {  
  6.     public  static SessionContext sessionContext=SessionContext.getInstance();  
  7.     public void sessionCreated(HttpSessionEvent httpSessionEvent) {  
  8.         sessionContext.AddSession(httpSessionEvent.getSession());  
  9.     }  
  10.     public void sessionDestroyed(HttpSessionEvent httpSessionEvent) {  
  11.         sessionContext.DelSession(httpSessionEvent.getSession());  
  12.     }  
  13. }  
SessionContex類(使用單例模式)
  1. package com.cnpc.framework.utils;  
  2. import java.util.HashMap;  
  3. import javax.servlet.http.HttpSession;  
  4. public class SessionContext {  
  5.     private static SessionContext instance;  
  6.     private HashMap<String,HttpSession> sessionMap;  
  7.     private SessionContext() {  
  8.         sessionMap = new HashMap<String,HttpSession>();  
  9.     }  
  10.     public static SessionContext getInstance() {  
  11.         if (instance == null) {  
  12.             instance = new SessionContext();  
  13.         }  
  14.         return instance;  
  15.     }  
  16.     public synchronized void AddSession(HttpSession session) {  
  17.         if (session != null) {  
  18.             sessionMap.put(session.getId(), session);  
  19.         }  
  20.     }  
  21.     public synchronized void DelSession(HttpSession session) {  
  22.         if (session != null) {  
  23.             sessionMap.remove(session.getId());  
  24.             if(session.getAttribute("userid")!=null){  
  25.                 sessionMap.remove(session.getAttribute("userid").toString());  
  26.                 //session.invalidate();   
  27.             }  
  28.         }  
  29.     }  
  30.     public synchronized HttpSession getSession(String session_id) {  
  31.         if (session_id == null) return null;  
  32.         return (HttpSession) sessionMap.get(session_id);  
  33.     }  
  34.     public HashMap getSessionMap() {  
  35.         return sessionMap;  
  36.     }  
  37.     public void setMymap(HashMap sessionMap) {  
  38.         this.sessionMap = sessionMap;  
  39.     }  
  40. }  

3、使用者登入成功後,更新session Map,如重複登入,強制之前session過期

  1. public void sessionHandlerByCacheMap(HttpSession session){  
  2.         String userid=session.getAttribute("userid").toString();  
  3.         if(SessionListener.sessionContext.getSessionMap().get(userid)!=null){  
  4.             HttpSession userSession=(HttpSession)SessionListener.sessionContext.getSessionMap().get(userid);  
  5.             //登出線上使用者  
  6.             userSession.invalidate();             
  7.             SessionListener.sessionContext.getSessionMap().remove(userid);  
  8.             //清除線上使用者後,更新map,替換map sessionid  
  9.             SessionListener.sessionContext.getSessionMap().remove(session.getId());   
  10.             SessionListener.sessionContext.getSessionMap().put(userid,session);   
  11.         }  
  12.         else  
  13.         {  
  14.             // 根據當前sessionid 取session物件。 更新map key=使用者名稱 value=session物件 刪除map  
  15.                 SessionListener.sessionContext.getSessionMap().get(session.getId());  
  16.             SessionListener.sessionContext.getSessionMap().put(userid,SessionListener.sessionContext.getSessionMap().get(session.getId()));  
  17.             SessionListener.sessionContext.getSessionMap().remove(session.getId());  
  18.         }  
  19.     }  

4、spring MVC攔截器校驗session是否過期,如果過期,給出提示,並跳轉到登入介面。

    攔截器配置  

web.xml配置

  1. <init-param>
  2.             <description>Spring MVC配置檔案</description>
  3.             <param-name>contextConfigLocation</param-name>
  4.             <param-value>classpath:controller.xml</param-value>
  5.         </init-param>
controller.xml配置
  1. <mvc:interceptors>
  2.         <beanclass="com.cnpc.framework.interceptor.AuthInterceptor"/>
  3.     </mvc:interceptors>

攔截器authInterceptor
  1. package com.cnpc.framework.interceptor;  
  2. import java.io.PrintWriter;  
  3. import java.util.Map;  
  4. import javax.annotation.Resource;  
  5. import javax.servlet.http.HttpServletRequest;  
  6. import javax.servlet.http.HttpServletResponse;  
  7. import org.springframework.stereotype.Component;  
  8. import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;  
  9. import com.cnpc.framework.common.SessionContainer;  
  10. @Component("SpringMVCInterceptor")  
  11. public class AuthInterceptor extends HandlerInterceptorAdapter {      
  12.     @Override  
  13.     public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {  
  14.         request.setCharacterEncoding("UTF-8");  
  15.         response.setCharacterEncoding("UTF-8");   
  16.         response.setContentType("text/html;charset=UTF-8");  
  17.         //過濾登入、退出訪問  
  18.         String[] noFilters = new String[] { "/auth/login", "/auth/logout" };  
  19.         String uri = request.getRequestURI();  
  20.         boolean beFilter = true;  
  21.         for (String s : noFilters) {  
  22.             if (uri.indexOf(s) != -1) {  
  23.                 beFilter = false;  
  24.                 break;  
  25.             }  
  26.         }  
  27.         SessionContainer sessionContainer = (SessionContainer) request.getSession().getAttribute("SessionContainer");  
  28.         if (beFilter) {  
  29.             if (null == sessionContainer) {  
  30.                 //ajax方式互動  
  31.                 if (request.getHeader("x-requested-with") != null  
  32.                         && request.getHeader("x-requested-with").equalsIgnoreCase("XMLHttpRequest"))// 如果是ajax請求響應頭會有,x-requested-with;  
  33.                 {                     
  34.                     response.setHeader("sessionstatus", "timeout");// 在響應頭設定session狀態  
  35.                     return false;  
  36.                 }  
  37.                 // 未登入  
  38.                 PrintWriter out = response.getWriter();  
  39.                 StringBuilder builder = new StringBuilder();  
  40.                 builder.append("<scripttype=\"text/javascript\" charset=\"UTF-8\">");  
  41.                 builder.append("alert(\"頁面過期,請重新登入\");");  
  42.                 builder.append("window.top.location.href='/auth/logout';");  
  43.                 builder.append("</script>");  
  44.                 out.print(builder.toString());  
  45.                 out.close();  
  46.                 return false;  
  47.             } else {                      
  48.                 // 新增系統日誌  
  49.                 // -----------------------------------  
  50.                 // -----------------------------------  
  51.             }  
  52.         }  
  53.         Map paramsMap = request.getParameterMap();  
  54.         return super.preHandle(request, response, handler);  
  55.     }  
  56. }  

以上Sprring MVC攔截器在同伺服器以ajax方式互動時,前臺需做如下相應處理:
  1. //控制ajax請求,session超時處理頁面跳轉  
  2.  $.ajaxSetup({     
  3.        contentType:"application/x-www-form-urlencoded;charset=

    相關推薦

    如何做到同一個賬號同一時段只能登入一個

     在許多web專案中,需要禁止使用者重複登入。一般來說有兩種做法:          一是在使用者表中維護一個欄位isOnLine(是否線上),使用者登入時,設定值為true,使用者退出時設定為false,在重複登入時,檢索到該欄位為true時,禁止使用者登入。這種方法

    shiro 實現單使用者登入一個使用者同一時刻只能一個地方登入

    如果我們跟shiro的原始碼,我們可以看到。當用戶登入成功後,shiro會把使用者名稱放到session的attribute中,key為DefaultSubjectContext_PRINCIPALS_SESSION_KEY,這個key的定義是在shiro的org.ap

    既然CPU同一時間只能執行一個執行緒,為什麼存在併發問題

    一點小疑惑終於解開啦 1.CPU的時間是按時間片分的,而不是一個時間點,併發問題是由於CPU執行緒切換導致的。   現在假設有一段程式碼 if(i == 1) { i++;  //斷點1 system.out.print(i); } //斷點2   有兩個執

    mvc 簡單實現一個賬號只能一個地方登入

    1.在mvc專案中找到 Global.asax //保證同一次會話的SessionID 不變         protected void Session_Start(object sender, EventArgs e)         { }         pro

    php 實現同一個賬號同時只能一個人登入

    有點類似QQ,二臺電腦登入,一臺會把另一臺擠掉線,並提示其他地點登入資訊。一,實現原理1,使用者在電腦A登入,session資訊存放在redis當中,並將session_id存到mysql資料庫中。2,同一使用者在電腦B登入,驗證完使用者名稱和密碼後,將該使用者資訊從資料庫讀出,取得使用者在電腦A登入的ses

    java web開發一個帳號同一時間只能一個人登入(單點登入

    對於一個帳號在同一時間只能一個人登入,可以通過下面的方法實現: 1 .在使用者登入時,把使用者新增到一個ArrayList中 2 .再次登入時檢視ArrayList中有沒有該使用者,如果ArrayList中已經存在該使用者,則阻止其登入 3 .當用戶退出時,需要從該ArrayList中刪除該使用者,這又分為

    Springboot + redis+shiro 限制 同一賬號 同時 多處登入

    從網上看了很多解決方案,用的最多的 應當是SessionId 了。方案雖多,適合自己的才是最好的。 之前做了一個 線上使用者的統計 和 管理員 踢出啟用線上使用者的功能,因此我得到了一個啟發。程式是死的,人是活得,我可不可以定一些規則,讓程式 根據我的規定 來 執行。 思路: 1.定

    SpringMvc實現一個賬號只能一個地方登陸,其他地方強制下線

    一. 前言  在處理專案登入問題的時候,為了賬號的安全性以及資訊的同步性,有時我們需要做到同一個賬戶只允許在一處地方登入,如果一個賬戶在一個處地方登入之後,之後在另一個地方也使用同一個賬戶登入,則前一個登入的賬戶就強制下線; 做到這種效果的方式有很多種,比如使

    jfinal+H5的websocket 實現同一賬戶在不同地點不同電腦只能登陸一個(互相踢下線)

    公司專案需求,因為專案是開賬戶賣錢的,為了避免有的使用者開一個賬戶N個人用,所以要求A賬戶只能在一個地點登入,別人如果使用A賬戶在別的電腦或者地點登入後就會吧上一個人給踢下線,當然也可以讓後一個登入的人登入不了,這都是看你邏輯怎麼控制的。 效果類似是qq登入的效果,先來張

    .NET預設一個客戶端對同一個伺服器地址同時只能建立2個TCP連線

    做一個客戶端的測試小程式測試web service的併發處理。開始用async task做,不管建立多少個task,用netstat看同時只有兩個tcp連線。以為是async task的問題,改用BackgroundWorker和多執行緒都是同樣的問題,經google得知原來是.NET預設一個客戶端對同一個伺

    我的shiro之旅: 十二 shiro 踢出使用者(同一使用者只能一處登入)

    部落格已移至 http://blog.gogl.top 看了一下官網,沒有找到關於如何控制同一使用者只能一處登入的介紹,網上也沒有找到相關的文章。可能有些人會記錄使用者的登入資訊,然後達到踢出使用者的效果。這裡介紹一個更簡單的方法。 如果我們跟shiro的原始碼,我們可以

    Spring Boot + Spring Security 防止使用者在多處同時登入一個使用者同時只能登入一次)及原始碼分析

    網上很多文章的實現方法寫得比較複雜 這裡介紹一個簡單的方法。 實現 @Configuration @EnableWebSecurity public class SecurityConfiguration extends WebSecurityConfig

    VS2015 報“包含在 Image 和 Image 項組中。專案項不允許這樣,它只能屬於一個項組。”解決方法

        今天在開啟同事移交的VS2015專案時報錯:包含在 Image 和 Image 項組中。專案項不允許這樣做,它只能屬於一個項組。     百度了一下沒有找到解決方法。 也許是錯誤太低階,大神們都不願意記錄了。     解決方案:     1、根據VS的錯誤提示,找到

    redis實現session共享,解決一個賬號只能一個終端登陸

    一個賬號在a電腦登陸了,此時在b電腦登陸,就會將a踢下線,需要解決兩個問題: 一、確保賬號只能在一個地方登陸; 二、登陸後傳送訊息通知; 對於第一個問題,我們可以藉助於session儲存於redis之後,實現session在多個站點,多臺伺服器共享的情況下,統一通過sess

    執行這些代碼, Edit1只能輸入數字,小數點和負號,負號和小數點只能輸入一個,負號必須在最前,粘貼的數字必須完全正確.

    eat win method and length use double .text sel 執行這些代碼, Edit1只能輸入數字,小數點和負號,負號和小數點只能輸入一個,負號必須在最前,粘貼的數字必須完全正確. type TForm1 = class(T

    一個JAVA類文件中只能一個public類嗎?

    資料 pri blog 外部 title java get cnblogs bsp 測試代碼一: 1 public class Test { 2 public static void main(String[] args) { 3 4 }

    解決session只能一個瀏覽器訪問的問題

    set coo jsession prot name 題解 朝向 不同的 etc 做購物車的時候,我們都知道購買的東西會保存到session中,但是光這樣簡單的保存起來就會帶來一個問題,只能呢被同一個瀏覽器訪問到,如果用戶使用不同的瀏覽器進行訪問網頁的話肯定是會出問題的。下

    Android ListView中的item只能一個選中的問題和ListView與activity互動的問題

    Android中ListView中的item與activity的互動有幾種方式:可以用回撥,廣播等,下面的方式是動態廣播的方式 ListView中的item選中事件,比如有多個item,每個item中都有一個CheckBox,我們要只選擇其中的一個,這是我們需要給每個item設定一

    Python實現指令碼鎖功能,同時只能執行一個指令碼

    1. 檔案鎖 指令碼啟動前檢查特定檔案是否存在,不存在就啟動並新建檔案,指令碼結束後刪掉特定檔案。 通過檔案的判斷來確定指令碼是否正在執行。 方法實現也比較簡單,這裡以python指令碼為例 #coding=utf-8 # #   檔案鎖指令碼測試 # import os

    php設計單例模式,一次只能產生一個物件

    單例即單個物件,一次只能生產一個物件,程式碼如下 class Single{  // 定義一個類  static private $instance=null; // 宣告一個靜態屬性,因為靜態屬性的資料具有長期性 且僅能為類所呼叫 所以這個靜態屬性用來儲存物件。