1. 程式人生 > >Java使用HttpSessionListener同一賬號登入人數限制,踢出功能

Java使用HttpSessionListener同一賬號登入人數限制,踢出功能

前言:

參考:http://blog.csdn.net/liuyuan185442111/article/details/45422779

如有疑問可進行留言,我會抽空回覆

1.因為公司的專案提出需求,要求系統使用者同一賬號登入的session次數為3個,超出將踢出第一個使用者,開始考慮到了shiro、security這兩種安全框架,百度了一堆亂程式碼無法解決我的需求,由於時間有限和本身對這兩種框架不是太熟練所以,我決定使用session監聽器去實現我的專案需求,不多說看程式碼吧。

一、編寫class類實現監聽

提示:首先要實現 HttpSessionListener介面重寫它的兩個實現方法一個為建立一個為銷燬
package 
com.maple.scof.member.common; import com.maple.scof.business.acnotation.Id; import com.maple.scof.business.entity.MemberEntity; import com.maple.scof.member.util.EnumUtil; import com.maple.scof.member.util.SessionUtil; import lombok.extern.slf4j.Slf4j; import javax.servlet.ServletContext; import javax.servlet.http.*; import
java.util.HashMap; import java.util.List; import java.util.Map; @Slf4j public class MemberSessionListener implements HttpSessionListener{ @Override public void sessionCreated(HttpSessionEvent se) { } /** * 執行session 銷燬方法 */ @Override public void sessionDestroyed(HttpSessionEvent event) { HttpSession session = event.getSession(); ServletContext application = session.getServletContext(); //第一步將當前登入使用者的物件取出來
MemberEntity member=(MemberEntity) session.getAttribute(EnumUtil.sessionKey.USER.getValue()); if(member == null) return; //從ServletContext取出當前登入使用者對應的map 對應多個session Map<String,List<HttpSession>> userSessionLoginKeyMap = (Map<String,List<HttpSession>>)application.getAttribute(member.getMemberId()+""); if(userSessionLoginKeyMap != null){ //取出登入使用者對應的session集合 List<HttpSession> list = userSessionLoginKeyMap.get(member.getMemberId()+""); if(list != null && list.size() > 0){ //如果當前map裡面的登入session個數等於1那麼就將對應的map和當前session給刪除 if(list.size() == 1){ session.removeAttribute(EnumUtil.sessionKey.USER.getValue()); application.removeAttribute(member.getMemberId()+""); }else{ //如果大於一個,就將當前的session物件從集合裡面remove調 同時覆蓋ServletContext 存放的相應資訊 list.remove(session); session.removeAttribute(EnumUtil.sessionKey.USER.getValue()); userSessionLoginKeyMap.put(member.getMemberId()+"",list); application.setAttribute(member.getMemberId()+"",userSessionLoginKeyMap); } } } } }
備註:
1. 執行session.invalidate()時。 
2. session超時自動銷燬時。 
3. 執行session.setAttribute(“anyname”, “其他物件”)或session.removeAttribute(“anyname”)將listener從session中刪除時。只要不將listener從session中刪除,就可以監聽到session的銷燬。

二、web.xml檔案配置

<listener>
   <listener-class>com.maple.scof.member.common.MemberSessionListener</listener-class>
</listener>
三、登入controller
package com.maple.scof.member.controller;

import java.io.IOException;
import java.util.*;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import com.maple.scof.business.entity.MemberEntity;
import com.maple.scof.business.entity.UserOperateLog;
import com.maple.scof.business.service.portal.ITaskSMSService;
import com.maple.scof.business.service.portal.IMemberService;
import com.maple.scof.business.util.DataUtil;
import com.maple.scof.member.util.EnumUtil.HttpStatus;
import com.maple.scof.member.util.EnumUtil.sessionKey;
import com.maple.scof.member.util.JsonResult;
import com.maple.scof.member.util.JsonResultObject;
import com.maple.scof.member.util.RequestUtil;
import com.maple.scof.member.util.SessionUtil;
import com.maple.scof.member.util.ValidateCode;

import lombok.extern.slf4j.Slf4j;

@Controller
@Slf4j
public class CommonController {
@Autowired
private ServletContext servletContext;

/**
 * 會員登入
 * 
 * @param req
* @param resp
* @param param
* @return
*/
@RequestMapping(value = "/fengyeLogin", method = RequestMethod.POST)
@ResponseBody
public JsonResultObject<Object> memberLogin(HttpServletRequest req, HttpServletResponse resp,
      @RequestBody Map<String, String> param) {
   JsonResultObject<Object> resultJson = new JsonResultObject<Object>();
   String phone = param.get("phone");
   String password = param.get("password");
   String code = param.get("code");

   //判斷驗證碼是否正確 
   ******* 西安楓葉 ********

   // 獲取登入客戶端的IP地址
String clientIP = RequestUtil.getIpAddr(req);
   UserOperateLog userLog = new UserOperateLog(1, clientIP);
   try {
      Map<String, Object> result = fengeyService.loginMember(phone, password, userLog);
      MemberEntity member = (MemberEntity) result.get("member");
      if(member==null){
        ******* 西安楓葉 *******
      }else if(member.getBlackMemberId()!=0){
         ******* 西安楓葉 *******
      }else if (DataUtil.getString(result.get("flag")).equals("true")) {

         //登入成功將當前使用者物件放入session
HttpSession session = req.getSession();
         session.setAttribute(sessionKey.USER.getValue(), member);
         //同時獲取根據使用者id上下文的對應的map
Map<String,List<HttpSession>>  userSessionLoginKeyMap = (HashMap<String, List<HttpSession>>)servletContext.getAttribute(member.getMemberId()+"");
         if(userSessionLoginKeyMap == null){
             userSessionLoginKeyMap = new HashMap<String, List<HttpSession>>();
         }
         List<HttpSession> listCountLogin = userSessionLoginKeyMap.get(member.getMemberId()+"");
         if(listCountLogin == null){
            listCountLogin = new ArrayList<>();
         }
         //新增登入的session到集合
listCountLogin.add(session);

         //使用者如果只關掉了瀏覽器籤頁 重新登入那麼他的sessionId是一樣的
         //需要去掉重複session
listCountLogin = this.removeStringListDupli(listCountLogin);

         //web.xml配置獲取session限制個數
int loginCount = DataUtil.getInt(req.getSession().getServletContext().getInitParameter("loginCount").toString());

         //如果當前登入使用者session集合大於限制個數 取一個session 將它銷燬
if(listCountLogin != null && listCountLogin.size() > loginCount){
            HttpSession oldSession = listCountLogin.get(0);
            oldSession.invalidate(); //使oldSession失效    application.removeAttribute(oldSession.getId()); //將oldSession從application中移除
}

         //將新的session集合給map同時覆蓋servletContext 裡的map
userSessionLoginKeyMap.put(member.getMemberId()+"",listCountLogin);
         servletContext.setAttribute(member.getMemberId()+"",userSessionLoginKeyMap);

         ******** 西安楓葉 ******
      } else if (DataUtil.getString(result.get("flag")).equals("false")) {
         ******** 西安楓葉 ******
      } 
   } catch (Exception e) {
      ******* 西安楓葉 *****
   }
   return resultJson;
}


/*集合去重複*/
private List<HttpSession> removeStringListDupli(List<HttpSession> stringList) {
   Set<HttpSession> set = new LinkedHashSet<HttpSession>();
   set.addAll(stringList);
   stringList.clear();
   stringList.addAll(set);
   return stringList;
}
}

備註:這個蛋疼的需求,耗時一天半,這種實現方式僅適用於流量較小的網站,如果有一百萬人同時登陸,將對記憶體空間的資源佔用非常高,到那個時候可以使用redis快取或者我開始提到的shiro、security,另外感謝我的老大王總提供一些參考支援