天天看點

限制在同一台電腦上隻允許有一個使用者登入系統

在web應用系統中,出于安全性考慮,經常需要對同一用戶端登入的使用者數量和一個客戶同時在多個用戶端登陸進行限制。具體一點就是:

    1、在同一台電腦上一次隻允許有一個使用者登入系統,2、一個使用者在同一時間隻允許在一個用戶端登入。

    我最近做的一個系統就遇到了這樣的問題,本來系統已經開發完成了,但是安全測評沒有通過,就是因為沒有做這兩個限制。怎麼來做這樣的限制呢?我在網上找了很久,發現問這個問題的人很多,但是沒有找到特别清楚的答案。後來自己摸索着,看了一些書,終于找到解決辦法了。

    要解決這個問題實際上不難,對于高手來說可能都懶得去說了,但是對于不熟悉web程式設計的人來說可能會困擾很久。下面我把我的解決辦法說出來,供大家參考!

    先介紹一下我那個系統的背景:j2ee,tomcat,沒有用cookie。

    首先确定解決這兩個問題的基本思路:

    1、要解決同一台電腦上隻允許有一個使用者登入系統,隻有一個辦法。監視每一個連接配接的來源,如果發現有一個新的連接配接與某個已經存在的連接配接來自同一台電腦,則終止其中的一個(當然,也可以提醒使用者,讓他自己決定終止哪一個)。

    2、要禁止一個使用者賬号同時在不同的用戶端登入,隻有監視每一個連接配接的使用者賬号,如果發現一個新連接配接的使用者賬号跟某個已經存在的連接配接的使用者賬号相同,則自動将前一個終止(同樣,也可以讓使用者自己決定終止哪一個)。

    确定了基本思路以後,就要找具體辦法了。我最初的想法是在資料庫建立一張表,存放已登入使用者的使用者名、實體位址、Session id等資訊。當使用者登入時,與這張表裡面的資料進行比對,如果發現實體位址與表中的某條記錄相同,則表示是同一台用戶端上有多個使用者再登入,如果發現正在登入的使用者的使用者名與表中已有記錄相同而主機名不同,則表示是一個賬号同時在不同的用戶端使用。

    相信很多一開始遇到這個問題的人都會考慮這種解決辦法。但是這種辦法有很多問題,最主要的問題有兩個:第一是效率,每一次都要從資料庫裡面取資料進行比對。第二是使用者退出時需要删除表中的記錄,而當使用者非正常退出時,很難及時監測(後來發現其實有辦法監測)。

    後來在網上的某個文章裡面看到一位大俠提到用監聽器,隻是那位大俠說的太含糊,照他說的辦法根本無法解決。雖然無法解決,但是提供了一個思路。于是我找了一本書,仔細看了其中關于監聽器的部分。解決辦法就在其中了!!!

    監聽器的詳細介紹見我的下一篇博文,這裡先把解決辦法告訴大家:

監聽器可以監聽Session及其所包含的屬性,即Attribute。

是以我們要做的就是:

1、建立一個監聽器,實作HttpSessionAttributeListener接口,監聽每一個Attribute的增加、編輯、删除事件。監聽器中還要建立一個map,将所有的session放入這個map中。

2、在使用者登入時将使用者名、實體位址、Session id存到Session中去(可以建立一個使用者登入位址資料傳輸對象,我建立了一個UserSessionAdd類,裡面包含username,macAdd,sessionId三個屬性,使用者登入時将這個資料對象初始化,并存入到session中)。

3、每個新會話開啟時,在監聽器中對Session包含的屬性進行判斷,如果新增的屬性與map中已有session的使用者登入位址資料相同,則表示新會話與我們要做的兩個限制相沖突。将與之沖突的會話提取出來,銷毀掉!

這麼說,還是不夠清楚,下面看代碼:

web.xml

<listener>

        <listener-class>監聽器完整路徑</listener-class>

    </listener>

使用者登入位址資料傳輸對象:

public class UserSession {

    private String addr;

    private String sessid;

    private String username;

    public String getAddr() {

        return addr;

    }

    public void setAddr(String addr) {

        this.addr = addr;

    public String getSessid() {

        return sessid;

    public void setSessid(String sessid) {

        this.sessid = sessid;

    public String getUsername() {

        return username;

    public void setUsername(String username) {

        this.username = username;

}

使用者登入的代碼:

    HttpSession session = request.getSession();

                    String userHost = request.getRemoteHost();  

                    String sessionId = request.getSession().getId();  

                    UserSession userSession = new UserSession();  

                    userSession.setUsername(user.getUsername());  

                    userSession.setSessid(sessionId);  

                    userSession.setAddr(userHost);  

                    request.getSession().setAttribute("userSession",userSession);

監聽器代碼:

import java.util.HashMap;

import java.util.Map;

import javax.servlet.http.HttpSession;

import javax.servlet.http.HttpSessionAttributeListener;

import javax.servlet.http.HttpSessionBindingEvent;

public class LoginListenner implements HttpSessionAttributeListener {

    Map<String, HttpSession> map = new HashMap<String, HttpSession>();

    public void attributeAdded(HttpSessionBindingEvent event) {

        String name = event.getName();

        if (name.equals("userSession")) {

            UserSession userSession = (UserSession) event.getValue();

            if (map.get(userSession.getUsername()) != null) {

                HttpSession session = map.get(userSession.getUsername());

                session.removeAttribute("userSession");

                session.invalidate();

            }

            map.put(userSession.getUsername(), event.getSession());

        }

    public void attributeRemoved(HttpSessionBindingEvent event) {

            map.remove(userSession.getUsername());

    public void attributeReplaced(HttpSessionBindingEvent event) {

        // TODO Auto-generated method stub