最近着手解决同一用户同时登录的问题,记录一下解决方案
要求:
1.A用户已登录系统
2.A用户再次登录,踢掉第一次登录的用户
上网查阅了不少帖子,没有找到既好用又简单的方法。
只能自己琢磨了,想了2种场景的方案。
1.如果不需要分布式部署,方案很简单:
定义一个存放用户session的全局静态Map<用户id,session对象>。
用户登录时,
如果发现map中已存在用户的session,则把map中的session取出来,调用session.invalidate();使session失效。
把当前session放入Map<用户id,session对象>中,
HttpSession s = request.getSession();
if(map.containsKey(userid)){
HttpSession session = (HttpSession)map.get(userid);
session.invalidate();
}
map.put(userid, s);
2.分布式部署环境,session在多个应用共享,但是各应用之间的全局静态Map<用户id,session对象>无法共享,因此上面的方案不可行。
第一种思路,把session存放在各应用都可以获取的地方,考虑持久化,但是HttpSession本身不是序列化的,不能把它持久化。Tomcat提供了对session的持久化。但是我在开发时使用的是tomcat,发布时却要用jboss,因为使用的应用服务器是不确定的,因此无法采用这种方案。
第二种思路,既然很难持久化session,那就不去做。场景1的方案是在用户第二次登录时,主动找到第一次的session,然后使它失效,但现在的问题是很难取到第一次的session,因为它没有被持久化。
那就换一种思路:做一个filter拦截所有请求(除了不需要验证用户是否已登录的请求,如:登录),在filter中验证session是否有效。如果无效,则使session失效。如何判断session是否有效呢?在登录时,给session中设置一个标记,
LoginController.java
session.setAttribute(CurrentThreadContext.CURR_USER_ID, userName);
session.setAttribute(LoginConstant.SESSION_USER_CODE, random);
我这里的random是一个随机数,同时把random持久化,比如存入数据库,或者文件,缓存等,我这里暂时存在了全局变量里
LoginConstant.sessionMap.put(userName, random);
filter验证session是否有效,对比A用户session中的random和持久化的random,如果相同,则有效,否则无效,使session失效。
LoginFilter.java
好了,验证一下,HttpSession session = httpRequest.getSession(false); if (session != null) { String userName = (String) session.getAttribute(CurrentThreadContext.CURR_USER_ID); String sessionUserCode = (String)session.getAttribute(LoginConstant.SESSION_USER_CODE); if(StringUtils.isEmpty(userName) || StringUtils.isEmpty(sessionUserCode) || !sessionUserCode.equals(LoginConstant.sessionMap.get(userName))){ //session中用户名为空 或 session验证码为空 或 验证码不正确,销毁session session.invalidate();//使session失效 } }
1.PC1使用A登录,session中存的random=1234,持久化的random=1234,发送请求时,session有效。
2.PC2使用A登录,session中存的random=6789,持久化的random=6789,发送请求时,session有效。
3.PC1发送请求,session中的random=1234,持久化的random=6789,session无效,session.invalidate();//使session失效,返回登录页面。
4.PC2发送请求,session有效。
ok。这里需要的就是加一个filter过滤请求。
如果有什么问题或者好的方案,请拍砖交流,共同学习