1.問題描述
同一個帳號只允許一個人登入,在有其他使用者登入的時候,給予原登入使用者警告提示。
2.分析設計
原問題分解成2個小問題:A. 唯一登入;B.踢人
A.
(1)第一次發生登入行為的時候的時候將使用者資訊及sessionId存放到一個實體Bean裡,並將這個bean儲存到session中。同時以該使用者帳號account作為key,帳號對應的sessionId作為值,存到一個Hashtable比如ht裡面,並把這個ht儲存到ServletContext(暨application)裡面永久儲存。
(2)再次發生登入行為的時候,同樣首先把使用者資訊及其sessionId存到實體Bean裡面並儲存到session。然後從ServletContext裡面取出ht,以account做為查詢條件判斷ht裡面是否包含著這個值。如果包含並且其對應的sessionId與當前session裡面儲存的實體Bean所帶的sessionId相同的話,則驗證通過,使用者的登入是有效;否則ht裡麵包含account值但sessionId與session裡面儲存的bean所帶的sessionID不同,則表明發生了同一賬戶登入行為,將原來的session登出,並更新ht把新的sessionId存放進去。
(3)實現頁面每次重新整理都會進行(2)的核對,這樣就實現了唯一登入
B:
按道理來說,A已經實現了部分的踢人原理,因為原登入使用者在重新整理頁面的時候,就發現自己被登出了session處於未登入狀態。但是在這基礎上,客戶要求發生有人用同一賬戶登入就立即通知原使用者,並中止原使用者的一切行為。
初步考慮有兩種方案:
(1)線程:啟用線程式控制制登入使用者
(2)JavaScript指令碼實現:很簡單,就是在比較短的時間內,不斷的發生重新整理頁面的行為,進行檢測,一旦發生sesionId被更改的情況,就彈出對話方塊警告使用者,並強制重新整理頁面。
a.頁面不斷被重新整理是很討厭的事情,出此考慮,在每個頁面嵌入一個隱藏的iframe,iframe連結到一個頁面,js指令碼控制不斷重新整理iframe頁面。這樣對於使用者來說是不透明的。除了滑鼠的閃動之外,使用者的頁面是不會發生變化的。而且iframe所連結到的頁面僅僅涉及到變數檢測,無比較耗費時間的檢測,伺服器的承受力可以接受。
b.在js的控制下,可以設定登入後多少時間提示使用者,這個時間越長,比如延長到10秒之後,那麼對伺服器及使用者的映像幾乎可以忽略。經測試,5秒中後提示給使用者的感覺就是立即被踢下去了。
3.部分源碼
初步考慮有兩種方案:
登陸時候的操作:
/**
* 使用者唯一登陸
* @param mb
* @param session
* @param account
*/
public void setService(memberBean mb, HttpSession session, String account) {
session.setAttribute(sessionName, mb);
if (sc.getAttribute(sessionList) == null) {// 第一次登陸
Hashtable htb = new Hashtable();
htb.put(account, session.getId());
sc.setAttribute(sessionList, htb);
} else {// 第二次登陸後第一次無效
Hashtable htb = (Hashtable) sc.getAttribute(sessionList);
String key = null;
if (htb.containsKey(account)) {
//更新session
htb.remove(account);
}
htb.put(account, session.getId());
sc.setAttribute(sessionList, htb);
}
}
唯一檢測:
/**
* 檢測使用者是否登陸
* @param session
* @param account
* @return
*/
public boolean checkLogin(HttpSession session,String account) ;
iframe的控制:
<script language="JavaScript" type="text/javascript">
<!--
var counter;
function iCheckCount() {
nowTime = new Date().getTime();
window.frames["iCheck"].src="iCheck.jsp?"+nowTime;
window.frames["iCheck"].window.location.reload();
counter=setTimeout("iCheckCount()", 5000);
}
function stopTimer() {
clearTimeout(counter);
}
function startTimer() {
counter=setTimeout("iCheckCount()", 5000);
}
//-->
</script>
4.總結及最佳化
踢人的解決方案裡,調用了IE視窗的不斷重新整理,雖然動作行為被隱藏,但是還是存在的。除此之外能不能線上程裡做到與用戶端互動,徹底的隱藏檢測行為,並能夠在發生同樣登入的情況下喚醒線程,控制用戶端視窗,是值得考慮的問題。
歡迎大家留言討論。