前言:
如有疑问可进行留言,我会抽空回复
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,另外感谢我的老大王总提供一些参考支持