Web專案防止同一賬號在不同session下重複登入
阿新 • • 發佈:2018-12-14
一、session簡介
瀏覽器在請求伺服器時,伺服器都會建立一個session,session負責瀏覽器與伺服器之間的會話。session的存在是為了維護瀏覽器和伺服器之間互動時保留一些互動資料,例如使用者行為或者使用者資訊等。每個會話都有自己的唯一標識,叫做sessionId。這個流程如下:- 瀏覽器請求伺服器。
- 伺服器檢查瀏覽器是否攜帶sessionId,如果未攜帶sessionId或者攜帶一個不存在的sessionId,伺服器生成一個session,並將sessionId返回給瀏覽器。
- 瀏覽器將sessionId存放在cookie中。
- 瀏覽器接下來訪問該伺服器時,就會帶著sessionId去訪問伺服器,伺服器都會找到處理該會話的session物件。直到session過期或者session被銷燬。
session有自己的過期時間(這個時間是服務氣端設定的),如果瀏覽器長時間未訪問伺服器,session會自動中斷。(session的存在是因為Http協議是一種無狀態協議,每次服務端接收到客戶端的請求時,都是一個全新的請求,伺服器並不知道客戶端的歷史請求記錄)。
二、使用者身份驗證
幾乎每一個網站針對不同身份的使用者會有不同的呈現,例如:遊客、登入使用者、管理員。這種情況就需要我們在基於session的基礎上再實現一套使用者身份的認證。這種身份認證通常是網站在需要使用者資訊認證時,使用者輸入用賬號,密碼來實現的。三、不同session下登入同一賬號
用在用不同瀏覽器訪問同一個網站時,登入同一個使用者賬號,這種情況下就會出現“不同session下重複登入賬號”。在現實中的業務中,很多時候,我們是不允許使用者在多處登入同一賬號的。處理這個需求的思路:
- 每當伺服器端生成一個新的session時,將sessionId和session物件分別作為key和value儲存起來(可用一個全域性的map,也可以用redis儲存)。
- 使用者登入賬號時,檢查伺服器端的全部session,檢查是否有該使用者標識的session,如果有,就將之前的session關閉。
- 關閉之前的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);
}