spring实现同一账号同一时间只能在同一个地方登录
这里先说一下大致思路:当系统验证用户的账号密码通过后,获取该用户的sessionid(每一个用户创建的sessionid是唯一的),和用户id(用户的唯一标识,用账号也可以),讲Userid作为键,sessionid作为值放入map中,再将map放入application,如果此后有其他人用同一个账号登录,将用户信息放入map后,会将先前的map覆盖掉(因为useid相同),写一个过滤器,用户操作时判断用户的sessionid是否于map中的sessionid相同,如果相同则继续执行,如果不同,意味着该用户被挤掉了,移除该用户的session,具体步骤如下
1、登录验证通过后,将该用户的id和sessionid方式Map中
//得到该用户的id
String strLoginUserId = objUserInfo.getUserid();
//判断该用户是否已经异地登录,如果异地登录,消除先前的session
Map<String,String> map=application.getAttribute("listUserSession")==null
?new HashMap<String,String>():(Map<String,String>)application.getAttribute("listUserSession");
String sesssionid=session.getId();
map.put(strLoginUserId, sesssionid);
application.setAttribute("listUserSession", map);
2、写过滤器,判断用户sessionid是否与map中session相同
过滤器
<filter>
<filter-name>sessionFilter</filter-name>
<filter-class>com.sps.util.SessionFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>sessionFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
判断session
// 执行过滤
// 从session中获取登录者实体
//获取application
HttpSession session=request.getSession();
//获取applicaiton
ServletContext application=session.getServletContext();
//获取session的值
LoginSysOrganInfo objLogin = (LoginSysOrganInfo) session.getAttribute("loginuser");
//获取application的值
Map<String,String> map=(Map<String,String>)application.getAttribute("listUserSession");
if (objLogin != null) {
//判断是否出现异地登录
boolean flag=false;
if(map!=null&&objLogin!=null){
String userid=objLogin.getUserid();
String sessionid=session.getId();
//便利map,推荐,对容量较大的很实用
for(Map.Entry<String, String> tempMap:map.entrySet()){
if(userid.equals(tempMap.getKey()))
//sessionid不等于map里面的出现异地登录
if(!(sessionid.equals(tempMap.getValue()))){
flag=true;
session.removeAttribute("loginuser");
break;
}
}
if(flag){
//获取out
PrintWriter out = response.getWriter();
out.print("<script charset='UTF-8'>");
out.print(" alert('The user login in other place!');");
out.print(" window.top.location.href = '" + loginUrl+ "';");
out.print("</script>");
return;
}
}
}
3、如果用户退出移除map中该用户的信息
HttpSession session = req.getSession();
ServletContext application=session.getServletContext();
//退出时需要移除该map里面该用户的信息
Map<String,String> map=(Map<String,String>)application.getAttribute("listUserSession");
LoginSysOrganInfo objLogin = (LoginSysOrganInfo) session.getAttribute("loginuser");
if(map!=null&&objLogin!=null){
String userid=objLogin.getUserid();
//遍历map,推荐,对容量较大的很实用
for(Map.Entry<String, String> tempMap:map.entrySet()){
if(userid.equals(tempMap.getKey())){
map.remove(tempMap.getKey());
break;
}
}
4、可能遇到的问题
情况一:如果用户没点击退出而是直接关闭了浏览器
情况二:存放用户信息的session超时
这两种情况和步骤3不同,系统无法移除applicaiotn中Map已退出用户的信息,如果用户量过大时,太占用服务器内存,解决办法有两个
方法1:设置一个定时器,隔一段时间清楚一次application
缺点:如果清除application时(这里为凌晨3点)出现同一账号同一时间多次登录,系统无法判断,之所以设置凌晨3点,是因为此时登录的情况比较少
//每天凌晨3点执行,
@Scheduled(cron = "0 0 3 * * ?")
public void deletelog() throws NormalException {
try {
//清理application,释放服务器压力
WebApplicationContext webApplicationContext=ContextLoader.getCurrentWebApplicationContext();
ServletContext application=webApplicationContext.getServletContext();
Map<String,String> map=(Map<String,String>)application.getAttribute("listUserSession");
application.removeAttribute("listUserSession");
} catch (Exception e) {
throw new NormalException(e.getMessage(), e);
}
}
方法二:用户每次登录时用System.currentTimeMillis()得到时间,将userid作为map的键,把sessionid及time放入对象中作为Map的值,设计一定时器,根据需要设计定时器执行的间隔,再次得到系统时间,减去对象中用户登录的时间,大于某个值(用户session存在的时长),移除map中该用户的信息,这里不做过多描述,大家可以尝试尝试
缺点:合理设计定时器的间隔,如果定时器间隔太短,对服务器压力太大,如果有一个机制让用在距用户登录时(每个用户登录时,定时器只能设定多长时间执行一次,不能动态设定在某个时间之后自动执行)多长时间后执行,那就太好了