Java使用HttpSessionListener同一賬號登入人數限制,踢出功能
阿新 • • 發佈:2019-01-02
前言:
參考:http://blog.csdn.net/liuyuan185442111/article/details/45422779
如有疑問可進行留言,我會抽空回覆
1.因為公司的專案提出需求,要求系統使用者同一賬號登入的session次數為3個,超出將踢出第一個使用者,開始考慮到了shiro、security這兩種安全框架,百度了一堆亂程式碼無法解決我的需求,由於時間有限和本身對這兩種框架不是太熟練所以,我決定使用session監聽器去實現我的專案需求,不多說看程式碼吧。
一、編寫class類實現監聽
提示:首先要實現 HttpSessionListener介面重寫它的兩個實現方法一個為建立一個為銷燬
packagecom.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.*; importjava.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,另外感謝我的老大王總提供一些參考支援