知道了session混亂產生的原因之後,也就知道了問題的根源。同時也引出了很多的問題:
1、如何記錄住線上人員(這裡只有帳號的系統使用者,不包括訪客);
2、如何限制同一個帳號在同一時間段內只能夠登陸一次系統?
3、如何限制不同的使用者在同一台機器上登陸系統?
4、管理員如何踢人?
我們首先來分析上面的問題:
首先在伺服器端當使用者通過身分識別驗證成功登陸系統之後,我們將此使用者的資訊記錄住(OnLineUserManager.java),包括帳號、登陸日期、機器的IP地址、session的ID,session對象。(記住session的ID極其重要,因為伺服器識別session是根據id,只要id相同,伺服器就認為是同一個使用者);
(上面解決了問題1)
這樣當使用者登陸系統的時候我們首先根據帳號判斷使用者是否登陸了,如果已經登陸,提示使用者;(這樣就解決了問題2)
如果未登陸,判斷使用者的session的id是否已經在使用者的資訊OnLineUserManager裡面了,如果是提示使用者
關閉當前視窗,重新點擊IE開啟一個新的瀏覽器視窗。(這樣session就不會混亂了)。
如果要限制不同的使用者在同一台機器上登陸系統?這個就要根據IP地址來判斷了。如果OnLineUserManager中
有通過這個機器登陸系統的使用者,那麼就提示使用者同一台機器只能夠一個帳號登陸;
(問題3也就解決了,注意:如果使用者使用了Proxy 伺服器,那麼此方法失效。這個方法適用於管理規範的使用者,客戶在區域網路內使用,每個客戶有固定的ip。)
問題4如何踢人?你想想OnLineUserManager中記錄了使用者session對象,只要根據使用者的帳號找到對應的
session對象,然後session.invalidate();這樣就可以徹底的將搗亂的人提出系統了。
===============需要注意的是OnLineUserManager必須是安全執行緒的=我的實現如下==============
package com.work.qxgl.login;import java.util.Vector;import org.apache.commons.logging.Log;import org.apache.commons.logging.LogFactory;import com.work.util.DateUtil;/** * 統計線上使用者數。前提是登入的時候限制一個使用者只能夠在系統中登入一次。 有了這個功能,管理員就可以管理線上使用者,如果誰不服從管理,就可以從系統中踢出去。 * TODO 將jsp放到WEB-INF後面,然後所有的URL必須通過struts的action調用。 使用攔截器Interceptor來實現許可權的控制! * 或者通過web中的Filter來實現許可權控制! 實現許可權管理系統日誌的記錄! * * @author wangmingjie * */public class OnLineUserManager {private static Log log = LogFactory.getLog(OnLineUserManager.class);private Vector<OnLineUser> users = null;private OnLineUserManager() {users = new Vector<OnLineUser>();//在建構函式中初始化}static class SingletonHolder {static OnLineUserManager instance = new OnLineUserManager();}/** * 單例模式。這樣簡單而且能夠保證安全執行緒。 * * @return */public static OnLineUserManager getInstance() {return SingletonHolder.instance;}/** * 擷取到登入使用者的數量。 * * @return */public synchronized int getCount() {users.trimToSize();return users.capacity();}/** * 通過使用者帳號判斷該使用者是否存在! 必須保證是安全執行緒的。 * * @param userAccount * @return */public synchronized boolean existUser(String userAccount) {users.trimToSize();boolean existUser = false;for (int i = 0; i < users.capacity(); i++) {if (userAccount.equals(((OnLineUser) users.get(i)).getUserAccount())) {existUser = true;break;}}return existUser;}/** * @param sessionid * @return */public synchronized boolean existSession(String sessionid) {users.trimToSize();boolean existUser = false;for (int i = 0; i < users.capacity(); i++) {if (sessionid.equals(((OnLineUser) users.get(i)).getSessionId())) {existUser = true;break;}}return existUser;}/** * 刪除使用者 * * @param userAccount * @return */public synchronized boolean deleteUser(String userAccount) {users.trimToSize();if (existUser(userAccount)) {int currUserIndex = -1;for (int i = 0; i < users.capacity(); i++) {if (userAccount.equals(((OnLineUser) users.get(i)).getUserAccount())) {currUserIndex = i;break;}}if (currUserIndex != -1) {users.remove(currUserIndex);users.trimToSize();log.debug("使用者" + userAccount + "退出系統"+ DateUtil.getCurrentDateTime());log.debug("線上使用者數為:" + getCount());return true;}}return false;}/** * 根據使用者帳號,擷取線上使用者資訊 * @param userAccount * @return */public synchronized OnLineUser getUser(String userAccount) {users.trimToSize();if (existUser(userAccount)) {int currUserIndex = -1;for (int i = 0; i < users.capacity(); i++) {if (userAccount.equals(((OnLineUser) users.get(i)).getUserAccount())) {currUserIndex = i;break;}}if (currUserIndex != -1) {return users.get(currUserIndex);}}return null;}/** * 擷取到線上使用者的資訊。 * * @return */public synchronized Vector<OnLineUser> getOnLineUser() {return users;}public synchronized void addUser(OnLineUser onLineUser) {users.trimToSize();if (!existUser(onLineUser.getUserAccount())) {users.add(onLineUser);log.debug(onLineUser.getUserAccount() + "/t登入到系統/t" + DateUtil.getCurrentDateTime());// 通過request才能夠擷取到使用者的ip等資訊} else {log.debug(onLineUser.getUserAccount() + "已經存在");}log.debug("線上使用者數為:" + getCount());}}
==================OnLineUser.java============================
package com.work.qxgl.login;import java.io.Serializable;import javax.servlet.http.HttpSession;/** * @author wangmingjie * @date 2008-6-30下午04:56:37 */public class OnLineUser implements Serializable {/** * */private static final long serialVersionUID = 5461473880667036331L;private String userId; //使用者idprivate String userAccount; //使用者帳號private String userName; //使用者名稱稱private String loginTime; //登陸時間戳記private String sessionId; //session的IDprivate String userIp ;//ip地址private HttpSession session; //記住session對象,測試能否用來將人員踢出系統public String getUserId() {return userId;}public void setUserId(String userId) {this.userId = userId;}public String getUserAccount() {return userAccount;}public void setUserAccount(String userAccount) {this.userAccount = userAccount;}public String getUserName() {return userName;}public void setUserName(String userName) {this.userName = userName;}public String getSessionId() {return sessionId;}public void setSessionId(String sessionId) {this.sessionId = sessionId;}public String getUserIp() {return userIp;}public void setUserIp(String userIp) {this.userIp = userIp;}public HttpSession getSession() {return session;}public void setSession(HttpSession session) {this.session = session;}public String getLoginTime() {return loginTime;}public void setLoginTime(String loginTime) {this.loginTime = loginTime;}public String toString(){return "OnLineUser{userId="+userId+",userAccount="+userAccount+",userName"+userName+",loginTime="+loginTime+",userIp="+userIp+",sessionId="+sessionId+"}";}//===============下面的資料只有在系統登陸日期中記錄==================================//private String logoutTime;//退出時間戳記;//private String logoutType;//退出方式 “session逾時退出”;“1主動退出”//private String lastAccessedTime;// 最後訪問時間}