天天看點

Session管理之逾時設定和強制下線

關于Session,在Java Web開發中,為我們提供了很多友善,Session是由浏覽器和伺服器之間維護的。好吧,閑話不多說,下面讓我們一步一步來實作它們。

(一)首先來說下Session逾時時間設定的三種方式,這些相對來說比較簡單:

(1)在web.xml中設定session-config

<session-config>
  <session-timeout>2</session-timeout>
 </session-config>
           

即互動間隔時間最長為2分鐘(該處時間機關為分鐘),2分鐘後session.getAttribute()擷取的值為空。

(2)在Tomcat的/conf/web.xml中session-config,預設值為:30分鐘

<session-config>
     <session-timeout>30</session-timeout>
</session-config>
           

同上,時間機關為分鐘。

(3)在Servlet中設定

HttpSession session = request.getSession();
session.setMaxInactiveInterval(60);
           

即在你的程式代碼中手動設定(該處時間機關為秒)。

優先級:Servlet中設定 >web.xml設定 > Tomcat/conf/web.xml設定

(二)同一使用者強制下線

大家都知道在目前很多的web項目中,大多數情況下都是可以讓同一個使用者賬号在不同的登入入口登入的,但這樣其實就顯得不是很嚴謹了,畢竟資訊修改等操作對于資訊是否能完全即時同步還是個未知之數。是以,接下來,我要做的隻是對于不同浏覽器的同一個使用者賬号的強制下線處理,對于同一個浏覽器暫不做考慮,先來看下面這張圖。大概的了解一下:

Session管理之逾時設定和強制下線

image

從上面可以看出:同一個浏覽器對于不同的賬号,登入時會産生相同的sessionId,這也就導緻了使用者之間資訊的覆寫;不同浏覽器對于不同的賬号登入時,登入時會産生不同的sessionId,這也就給了我們可操作的空間了,正是要利用這一點來進行判斷和相應處理。

(1)添加監聽器

為了友善這裡使用Session監控的方式,建立SessionListener,如下:

public class SessionListener implements HttpSessionBindingListener{

    @Override
    public void valueBound(HttpSessionBindingEvent event){
        // TODO Auto-generated method stub

    }
    @Override
    public void valueUnbound(HttpSessionBindingEvent event){
        // TODO Auto-generated method stub

    }
}
           

這裡HttpSessionBindingListener和HttpSessionListener效果一樣,大家可以任選其一。重載兩個方法:session的建立和銷毀。

當然,在web.xml中添加相應配置是必不可少的:

<listener>
   <listener-class>com.yoki.util.SessionListener</listener-class>
</listener>
           

由于sessionId和userId需要存儲,友善後面的判斷,我們在上面的類中添加兩個Map,如下:

//儲存username和session的映射
public static HashMap<String,Session> MAP1 = new HashMap<String,Session>();
//儲存sessionID和username的映射
public static HashMap MAP2 = new HashMap();
           

最後,使用者登入驗證成功時需要調用一個方法來判斷是否強制下線:

public static void userLogin(Session session,String sUserName){
    //已登入
    if(MAP2.containsValue(sUserName)){
       Session l_session = MAP1.get(sUserName);
       //不同浏覽器,同一使用者(強制下線前一個)
       if(l_session != null && l_session.getId() != session.getId()){
          MAP1.remove(sUserName);
          MAP2.remove(l_session.getId());
          l_session.setAttribute("msg", "您的賬号已在另一處登入!");
          MAP2.put(session.getId(), sUserName);
          MAP1.put(sUserName, session);
       }
       //同一浏覽器,同一使用者(不做任何變動)

    }else{
       //未登入
       if(MAP2.containsKey(session.getId())){
          //同一浏覽器,不同使用者(不做任何變動)
       }else{
          //不同浏覽器,不同使用者(正常添加)
          MAP2.put(session.getId(), sUserName);
          MAP1.put(sUserName, session);
       }
    }

}
           

解釋一下:這裡使用username和userId效果一樣,看你們怎麼友善怎麼用了,方法中的邏輯是根據上面的圖來編寫的,首先判斷使用者是否登入了,因為MAP中儲存了相關的session關聯資訊,是以可以通過這個來判斷;由于此處隻對不同浏覽器相同使用者進行處理,是以直接判斷是否是同一個浏覽器。方法的參數session是使用者在目前浏覽器登入時的資訊,我們可以從MAP中得到之前儲存過的相同使用者的session資訊,與之進行比較,裡面的邏輯是:移除MAP中儲存的之前的使用者資訊(對應的session此時未銷毀),并給其session添加一個msg資訊(後面用到,往下看),再添加新的使用者資訊。

上面的方法調用放在登入驗證成功後,各自項目不同,但登入驗證的類基本差不多:

SessionListener.userLogin(session, USERNAME);
           

(2)添加前端頁面調用的方法

在登入驗證的類中添加如下方法:

/*
 * 判斷使用者是否重複登入
 */
@RequestMapping(value="/checkUserOnline")
@ResponseBody
public void checkUserOnline(HttpServletRequest request,HttpServletResponse response) throws IOException{
    HttpSession session=request.getSession();  
    PrintWriter out = response.getWriter();
    String msg = "";
    if(session.getAttribute("msg") != null){
       msg = session.getAttribute("msg").toString();
       System.out.println(msg);
    }
    out.print(session.getAttribute("msg"));
}
           

方法中擷取之前添加到session中的msg,用來判斷是否強制下線,繼續。

(3)前端頁面循環調用

選擇一個頁面,最好是所有頁面都用到的,比如我用的index.jsp,如下:

<script type="text/javascript">
   $(document).ready(function(){
      setInterval("checkUserOnline()",5000); //每隔5秒判斷一次
   }

   function checkUserOnline(){
      var msg = "";
      $.ajax({
        type : "POST",
        url : "checkUserOnline",
        data : {},
        async: false,
        success : function(data){
           msg = data;
        }
      });
      if (msg == 'null' || msg == '' || msg == 'undefined'){
         return;
      }else{
         //調用你的登出使用者方法
         var url="<%=path%>/logout.do";
         $.get(url,function(data){});
      }
   }
</script>
           

js中調用setInterval方法,設定調用的方法和間隔時間,方法裡通過ajax調用上面添加的類并傳回msg,通過msg來判斷是否調用登出方法(路徑啥的自己注意,能調用到就ok)。

(4)登出

一般web項目登入進去後都會有個退出按鈕,點選即傳回到登入頁,此時在裡面添加一行代碼,防止錯誤,可能會出現重新登入報session已被銷毀的錯誤提示,但第二次便會成功,這裡便是為了消除該錯誤:

SessionListener.MAP2.remove(session.getId());
           

好了,基本的設定完成了,啟動項目,打開兩個不同的浏覽器,先登入一個使用者,成功後,在另一個浏覽器中登入相同的使用者,登入成功後,會在控制台上列印出msg:

您的賬号已在另一處登入!
           

此時,重新整理第一個浏覽器使用者登入界面,便會發現已經退出跳轉到登入頁了,大功告成!!!