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中该用户的信息,这里不做过多描述,大家可以尝试尝试

缺点:合理设计定时器的间隔,如果定时器间隔太短,对服务器压力太大,如果有一个机制让用在距用户登录时(每个用户登录时,定时器只能设定多长时间执行一次,不能动态设定在某个时间之后自动执行)多长时间后执行,那就太好了


    第一次写博客,代码整理的不是太好看,语言描述不太恰当,还望大家谅解 。。。