前言:
参考: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,另外感谢我的老大王总提供一些参考支持