1. 程式人生 > >Web專案防止同一賬號在不同session下重複登入

Web專案防止同一賬號在不同session下重複登入

一、session簡介

瀏覽器在請求伺服器時,伺服器都會建立一個session,session負責瀏覽器與伺服器之間的會話。session的存在是為了維護瀏覽器和伺服器之間互動時保留一些互動資料,例如使用者行為或者使用者資訊等。每個會話都有自己的唯一標識,叫做sessionId。這個流程如下:
  1. 瀏覽器請求伺服器。
  2. 伺服器檢查瀏覽器是否攜帶sessionId,如果未攜帶sessionId或者攜帶一個不存在的sessionId,伺服器生成一個session,並將sessionId返回給瀏覽器。
  3. 瀏覽器將sessionId存放在cookie中。
  4. 瀏覽器接下來訪問該伺服器時,就會帶著sessionId去訪問伺服器,伺服器都會找到處理該會話的session物件。直到session過期或者session被銷燬。

session有自己的過期時間(這個時間是服務氣端設定的),如果瀏覽器長時間未訪問伺服器,session會自動中斷。(session的存在是因為Http協議是一種無狀態協議,每次服務端接收到客戶端的請求時,都是一個全新的請求,伺服器並不知道客戶端的歷史請求記錄)。

二、使用者身份驗證

幾乎每一個網站針對不同身份的使用者會有不同的呈現,例如:遊客、登入使用者、管理員。這種情況就需要我們在基於session的基礎上再實現一套使用者身份的認證。這種身份認證通常是網站在需要使用者資訊認證時,使用者輸入用賬號,密碼來實現的。

三、不同session下登入同一賬號

用在用不同瀏覽器訪問同一個網站時,登入同一個使用者賬號,這種情況下就會出現“不同session下重複登入賬號”。在現實中的業務中,很多時候,我們是不允許使用者在多處登入同一賬號的。

處理這個需求的思路:

  1. 每當伺服器端生成一個新的session時,將sessionId和session物件分別作為key和value儲存起來(可用一個全域性的map,也可以用redis儲存)。
  2. 使用者登入賬號時,檢查伺服器端的全部session,檢查是否有該使用者標識的session,如果有,就將之前的session關閉。
  3. 關閉之前的session後,使用者的賬號資訊(或者能區分使用者身份的唯一標識)以引數的形式放在當前的session中。方便下次登入檢測。

具體實現 實現一個session管理類。

import javax.servlet.http.HttpSession;
import java.util.
HashMap; import java.util.Map; import java.util.Set; public class MySessionContext { private static MySessionContext instance; private HashMap<String,HttpSession> sessionMap; //建立儲存session的map集合 private MySessionContext() { sessionMap = new HashMap<String,HttpSession>(); } //獲取處理session的例項 public static MySessionContext getInstance() { if (instance == null) { instance = new MySessionContext(); } return instance; } //新增一個session到map public synchronized void addSession(HttpSession session) { if (session != null) { sessionMap.put(session.getId(), session); } } //刪除一個session到map public synchronized void delSession(HttpSession session) { if (session != null) { sessionMap.remove(session.getId()); } } //根據sessionId獲取一個session物件 public synchronized HttpSession getSession(String sessionID) { if (sessionID == null) { return null; } return sessionMap.get(sessionID); } //檢查是否有存在該使用者名稱的session public HttpSession checkExist(String username){ Set<Map.Entry<String,HttpSession>> entrySet = sessionMap.entrySet(); for(Map.Entry<String,HttpSession> entry : entrySet){ HttpSession session = entry.getValue(); Object usernameValue = session.getAttribute("usernameKey"); if(usernameValue != null && usernameValue.toString().equals(username)){ return entry.getValue(); } } return null; } }

實現一個session監聽類:


import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;

public class SessionListener implements HttpSessionListener {


    private MySessionContext myc = MySessionContext.getInstance();

	//當瀏覽器與伺服器之間的session被建立時,儲存該session。
    @Override
    public void sessionCreated(HttpSessionEvent httpSessionEvent) {
        HttpSession session = httpSessionEvent.getSession();
        myc.addSession(session);
    }

	//當session被銷燬時,刪除該session。
    @Override
    public void sessionDestroyed(HttpSessionEvent httpSessionEvent) {
        HttpSession session = httpSessionEvent.getSession();
        myc.delSession(session);
    }
}

將該session監聽類註冊到web.xml的監聽器列表:

<listener>
        <listener-class>com.msmk.cloud.listener.SessionListener</listener-class>
</listener>

在使用者登入驗證通過後呼叫下面這個方法處理session:


  /**
     * 專門用於處理單點登入的方法,如果使用者用同一個賬號登入過了,將之前的session銷燬。
     * @param request
     */
    public void handleSession(HttpServletRequest request){

        User user = userService.getCurrent();

        if(user == null){
            return;
        }

		//作為區分使用者的唯一標識。
        String usernameKey = "";

        //如果使用者名稱不為空
        if(!StringUtils.isEmpty(user.getUsername())){
            usernameKey = user.getUsername();
        }

        //如果使用者民為null,那就是第三方登入,使用otherId
        if(StringUtils.isEmpty(user.getUsername()) && (!StringUtils.isEmpty(user.getOtherUid()))){
            usernameKey = user.getOtherUid();
        }else{
            if(!StringUtils.isEmpty(user.getOpenid())){
                usernameKey = user.getOtherUid();
            }
        }

        //獲取session管理例項。
        MySessionContext myc= MySessionContext.getInstance();
        HttpSession httpSession = myc.checkExist(usernameKey);

        //如果sessionMap中存在該使用者標識的session,將前一個session關閉。
        if(httpSession != null){
            httpSession.invalidate();
        }

        //將代表使用者的唯一標識,大部分是username,由於第三方登入,有時也是otherId,或者openId
        HttpSession sessionCurrent = request.getSession();
        sessionCurrent.setAttribute("usernameKey",usernameKey);
    }