文档章节

Session管理之超时设置和强制下线

海岸线的曙光
 海岸线的曙光
发布于 03/30 17:24
字数 1513
阅读 4523
收藏 142
点赞 6
评论 13

    关于Session,在Java Web开发中,为我们提供了很多方便,Session是由浏览器和服务器之间维护的。好吧,闲话不多说,下面让我们一步一步来实现它们。

    (一)首先来说下Session超时时间设置的三种方式,这些相对来说比较简单:

    (1)在web.xml中设置session-config

<session-config>
  <session-timeout>2</session-timeout>
 </session-config>

    即交互间隔时间最长为2分钟(该处时间单位为分钟),2分钟后session.getAttribute()获取的值为空。

    (2)在Tomcat的/conf/web.xml中session-config,默认值为:30分钟

<session-config>
     <session-timeout>30</session-timeout>
</session-config>

    同上,时间单位为分钟。

    (3)在Servlet中设置

HttpSession session = request.getSession();
session.setMaxInactiveInterval(60);

    即在你的程序代码中手动设置(该处时间单位为秒)。

    优先级:Servlet中设置 >web.xml设置 > Tomcat/conf/web.xml设置

    (二)同一用户强制下线

    大家都知道在目前很多的web项目中,大多数情况下都是可以让同一个用户账号在不同的登录入口登录的,但这样其实就显得不是很严谨了,毕竟信息修改等操作对于信息是否能完全即时同步还是个未知之数。所以,接下来,我要做的只是对于不同浏览器的同一个用户账号的强制下线处理,对于同一个浏览器暂不做考虑,先来看下面这张图。大概的了解一下:

    从上面可以看出:同一个浏览器对于不同的账号,登录时会产生相同的sessionId,这也就导致了用户之间信息的覆盖;不同浏览器对于不同的账号登录时,登录时会产生不同的sessionId,这也就给了我们可操作的空间了,正是要利用这一点来进行判断和相应处理。

    (1)添加监听器

    为了方便这里使用Session监控的方式,创建SessionListener,如下:

public class SessionListener implements HttpSessionBindingListener{

    @Override
    public void valueBound(HttpSessionBindingEvent event){
        // TODO Auto-generated method stub
        
    }
    @Override
    public void valueUnbound(HttpSessionBindingEvent event){
        // TODO Auto-generated method stub
        
    }
}

    这里HttpSessionBindingListener和HttpSessionListener效果一样,大家可以任选其一。重载两个方法:session的创建和销毁。

    当然,在web.xml中添加相应配置是必不可少的:

<listener>
   <listener-class>com.yoki.util.SessionListener</listener-class>
</listener>

    由于sessionId和userId需要存储,方便后面的判断,我们在上面的类中添加两个Map,如下:

//保存username和session的映射
public static HashMap<String,Session> MAP1 = new HashMap<String,Session>();
//保存sessionID和username的映射
public static HashMap MAP2 = new HashMap();

    最后,用户登录验证成功时需要调用一个方法来判断是否强制下线:

public static void userLogin(Session session,String sUserName){
    //已登录
    if(MAP2.containsValue(sUserName)){
       Session l_session = MAP1.get(sUserName);
       //不同浏览器,同一用户(强制下线前一个)
       if(l_session != null && l_session.getId() != session.getId()){
          MAP1.remove(sUserName);
          MAP2.remove(l_session.getId());
          l_session.setAttribute("msg", "您的账号已在另一处登录!");
          MAP2.put(session.getId(), sUserName);
          MAP1.put(sUserName, session);
       }
       //同一浏览器,同一用户(不做任何变动)
       
    }else{
       //未登录
       if(MAP2.containsKey(session.getId())){
          //同一浏览器,不同用户(不做任何变动)
       }else{
          //不同浏览器,不同用户(正常添加)
          MAP2.put(session.getId(), sUserName);
          MAP1.put(sUserName, session);
       }
    }

}

    解释一下:这里使用username和userId效果一样,看你们怎么方便怎么用了,方法中的逻辑是根据上面的图来编写的,首先判断用户是否登录了,因为MAP中保存了相关的session关联信息,所以可以通过这个来判断;由于此处只对不同浏览器相同用户进行处理,所以直接判断是否是同一个浏览器。方法的参数session是用户在当前浏览器登录时的信息,我们可以从MAP中得到之前保存过的相同用户的session信息,与之进行比较,里面的逻辑是:移除MAP中保存的之前的用户信息(对应的session此时未销毁),并给其session添加一个msg信息(后面用到,往下看),再添加新的用户信息。

    上面的方法调用放在登录验证成功后,各自项目不同,但登录验证的类基本差不多:

SessionListener.userLogin(session, USERNAME);

    (2)添加前端页面调用的方法

    在登录验证的类中添加如下方法:

/*
 * 判断用户是否重复登录
 */
@RequestMapping(value="/checkUserOnline")
@ResponseBody
public void checkUserOnline(HttpServletRequest request,HttpServletResponse response) throws IOException{
    HttpSession session=request.getSession();  
    PrintWriter out = response.getWriter();
    String msg = "";
    if(session.getAttribute("msg") != null){
       msg = session.getAttribute("msg").toString();
       System.out.println(msg);
    }
    out.print(session.getAttribute("msg"));
}

    方法中获取之前添加到session中的msg,用来判断是否强制下线,继续。

    (3)前端页面循环调用

    选择一个页面,最好是所有页面都用到的,比如我用的index.jsp,如下:

<script type="text/javascript">
   $(document).ready(function(){
      setInterval("checkUserOnline()",5000); //每隔5秒判断一次
   }
   
   function checkUserOnline(){
      var msg = "";
      $.ajax({
        type : "POST",
        url : "checkUserOnline",
        data : {},
        async: false,
        success : function(data){
           msg = data;
        }
      });
      if (msg == 'null' || msg == '' || msg == 'undefined'){
         return;
      }else{
         //调用你的注销用户方法
         var url="<%=path%>/logout.do";
         $.get(url,function(data){});
      }
   }
</script>

    js中调用setInterval方法,设置调用的方法和间隔时间,方法里通过ajax调用上面添加的类并返回msg,通过msg来判断是否调用注销方法(路径啥的自己注意,能调用到就ok)。

    (4)注销

    一般web项目登录进去后都会有个退出按钮,点击即返回到登录页,此时在里面添加一行代码,防止错误,可能会出现重新登录报session已被销毁的错误提示,但第二次便会成功,这里便是为了消除该错误:

SessionListener.MAP2.remove(session.getId());

    好了,基本的设置完成了,启动项目,打开两个不同的浏览器,先登录一个用户,成功后,在另一个浏览器中登录相同的用户,登录成功后,会在控制台上打印出msg:

您的账号已在另一处登录!

    此时,刷新第一个浏览器用户登录界面,便会发现已经退出跳转到登录页了,大功告成!!!

© 著作权归作者所有

共有 人打赏支持
海岸线的曙光
粉丝 36
博文 42
码字总数 49715
作品 0
朝阳
程序员
加载中

评论(13)

kaole
kaole
单机部署情况下,使用的是hashMap来保存对应关系,所有是有并发问题的
同时hashmap也解决不了多机部署的情况
海岸线的曙光
海岸线的曙光

引用来自“海岸线的曙光”的评论

引用来自“穆哥哥”的评论

有没有办法实现跨浏览器的强制下线呢?

回复@穆哥哥 : 这个就是不同浏览器的

引用来自“穆哥哥”的评论

但是这个地方取的session可能会有不同步的问题。通过监听器的话,如果tomcat持久化了会话,再启动的时候,你会发现原来的登录信息,你的map里面没有,但是其实tomcat里面的这个是有的。:thumbsup:

引用来自“海岸线的曙光”的评论

嗯,这个在监听器的销毁方法里应该可以进行处理的

引用来自“穆哥哥”的评论

这个我之前处理过,不过好像没有什么好办法,我之前处理的方式有两种 ,一种就是直接不让tomcat持久化session信息,换而言之,也就是tomcat重新启动后,所有的登录信息都需要重来一遍。第二个就是持久化到数据库,这个时候不要监听器了,直接查询数据库来判断。。。不知道你还有没有其他更好的办法。:+1:
嗯,tomcat是否持久化根据自己需要来添加,暂时还没对这块深入研究:smile:
穆哥哥
穆哥哥

引用来自“海岸线的曙光”的评论

引用来自“穆哥哥”的评论

有没有办法实现跨浏览器的强制下线呢?

回复@穆哥哥 : 这个就是不同浏览器的

引用来自“穆哥哥”的评论

但是这个地方取的session可能会有不同步的问题。通过监听器的话,如果tomcat持久化了会话,再启动的时候,你会发现原来的登录信息,你的map里面没有,但是其实tomcat里面的这个是有的。:thumbsup:

引用来自“海岸线的曙光”的评论

嗯,这个在监听器的销毁方法里应该可以进行处理的
这个我之前处理过,不过好像没有什么好办法,我之前处理的方式有两种 ,一种就是直接不让tomcat持久化session信息,换而言之,也就是tomcat重新启动后,所有的登录信息都需要重来一遍。第二个就是持久化到数据库,这个时候不要监听器了,直接查询数据库来判断。。。不知道你还有没有其他更好的办法。:+1:
海岸线的曙光
海岸线的曙光

引用来自“海岸线的曙光”的评论

引用来自“穆哥哥”的评论

有没有办法实现跨浏览器的强制下线呢?

回复@穆哥哥 : 这个就是不同浏览器的

引用来自“穆哥哥”的评论

但是这个地方取的session可能会有不同步的问题。通过监听器的话,如果tomcat持久化了会话,再启动的时候,你会发现原来的登录信息,你的map里面没有,但是其实tomcat里面的这个是有的。:thumbsup:
嗯,这个在监听器的销毁方法里应该可以进行处理的
穆哥哥
穆哥哥

引用来自“海岸线的曙光”的评论

引用来自“穆哥哥”的评论

有没有办法实现跨浏览器的强制下线呢?

回复@穆哥哥 : 这个就是不同浏览器的
但是这个地方取的session可能会有不同步的问题。通过监听器的话,如果tomcat持久化了会话,再启动的时候,你会发现原来的登录信息,你的map里面没有,但是其实tomcat里面的这个是有的。:thumbsup:
海岸线的曙光
海岸线的曙光

引用来自“MiniDoraemon”的评论

你好 同一用户强制下线 那个图上的不同浏览器相同用户,应该 sessionId 不同,userId是相同的吧
感谢纠正,画图的时候写错了:+1:
MiniDoraemon
MiniDoraemon
你好 同一用户强制下线 那个图上的不同浏览器相同用户,应该 sessionId 不同,userId是相同的吧
海岸线的曙光
海岸线的曙光

引用来自“穆哥哥”的评论

有没有办法实现跨浏览器的强制下线呢?

回复@穆哥哥 : 这个就是不同浏览器的
穆哥哥
穆哥哥
有没有办法实现跨浏览器的强制下线呢?
f
freezingsky

引用来自“打破突破”的评论

可以详细说下为什么同一浏览器不同账号的访问会产生相同的sessenId么?
主要原因是来自于浏览器与Web服务器通讯的过程。
如果当前浏览器没有sessionId,则后端会生成新的。如果当前浏览器已经有了,则默认使用。所以,经常会发生一个现象就是,同一个浏览器中,A用户登录,然后再B用户登录,这时候,你切换到A用户的Tab,实现上,流浏览器与服务器维护的信息,已经变成了B,则A用户的信息已经丢失了。
不过,有一些浏览器做了改造,不同Tab间的cookie信息是独立的,就可以解决这一块的情况。
sigma敏捷版系列文章:kubernetes grace period 失效问题排查

我们在使用 Kubernetes 时遇到了设置 --grace-period 参数不生效的问题,从 kubelet 日志看是 kubelet 接受到 Pod DELETE 事件后在同一秒内又接受到了 REMOVE 事件,所以 Pod 立刻就会删掉了...

冬岛
07/11
0
0
kubernetes grace period 失效问题排查

我们在使用 Kubernetes 时遇到了设置 --grace-period 参数不生效的问题,从 kubelet 日志看是 kubelet 接受到 Pod DELETE 事件后在同一秒内又接受到了 REMOVE 事件,所以 Pod 立刻就会删掉了...

冬岛
07/10
0
0
转:session监听 HttpSessionBindingListener

转载自http://blog.sina.com.cn/s/blog_4dacfb010100qxf8.html 首先我在网上查了一下session的真正销毁条件: 1调用 session.invalidate();方法 2 session到了设置或者默认的超时时间,自动销...

程序员YB
2011/11/18
1K
0
使用shiro保护你的springboot应用

springboot中使用shiro大都是通过shiro-spring.jar进行的整合的,虽然不是太复杂,但是也无法做到spring-boot-starter风格的开箱即用。项目中经常用到的功能比如:验证码、密码错误次数限制、...

wangjie2016
01/05
0
1
基于 MySQL 的数据库中间件--Meituan-DBProxy

奇虎360公司开源的Atlas是优秀的数据库中间件,美团点评DBA团队针对公司内部需求,在其上做了很多改进工作,形成了新的高可靠、高可用企业级数据库中间件DBProxy,已在公司内部生产环境广泛使...

匿名
2017/01/06
1K
0
美团点评数据库中间件 DBProxy 开源

介绍 随着数据量的不断增大,传统的直连数据库对数据进行访问的方式已经无法满足一般公司的需求。通过数据库中间件,可以对数据库进行水平扩展,由原来单台数据库扩展到多台数据库,数据库中...

两味真火
2017/01/06
5K
11
VMware vRealize Operations 闲置 30 分钟后会出现 UI 会话超时

默认情况下,vRealize Operations 的 UI 会话超时为 30 分钟。 vRealize Operations 闲置 30 分钟后会出现 UI 会话超时。 要解决此问题,请更改标准和高级版本的会话超时时限,并将 session-...

qq8658868
07/03
0
0
springboot + shiro之登录人数限制、登录判断重定向、session时间设置

springboot + shiro之登录人数控制 项目 前篇:spring boot + mybatis + layui + shiro后台权限管理系统 本文是基于spring boot + mybatis + layui + shiro后台权限管理系统开发的,新增功能...

wyait
04/24
0
0
ASR9K-BNG PPPoE and IPoE

Topo: IXIA-------------(MOD80)ASR9K---------------IXIA BNG配置: -----------------PPPoE------------------- pool vrf default ipv4 PPP address-range 50.50.0.1 50.50.255.255 dynami......

kekeAlice
03/19
0
0
Java web.xml session-config 属性配置

在Java Web开发中,Session为我们提供了很多方便,Session是由浏览器和服务器之间维护的。Session超时理解为:浏览器和服务器之间创建了一个Session,由于客户端长时间(休眠时间)没有与服务...

Oscarfff
2015/06/12
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

ndarray花式索引

花式索引 花式索引(Fancy indexing)是一个NumPy术语,它指的是利用整数数组进行索引。假设我们有一个8×4数组: In [117]: arr = np.empty((8, 4))In [118]: for i in range(8): ....

火力全開
5分钟前
0
0
Mybaties报错Error querying database

Mybaties我们经常用到动态SQL,如下我们利用动态去做判断,这样写当然没问题,但是当我们不是去判断orgCode(本文中orgCode一直为String类型)是否为空而是判断orgCode是否是一个值的时候该怎...

王子城
7分钟前
0
0
Android 调用手机自带的下载器下载

亲测有用,原文下载地址: 原文地址:https://blog.csdn.net/weixin_36554045/article/details/79108796 下面是原文: 创建一个广播类 public class UpdataBroadcastReceiver extends Broad...

她叫我小渝
10分钟前
0
0
idea工具debug断点红色变成灰色,断点无效

来自:idea工具debug断点红色变成灰色 没事别瞎点,禁用了断点当然不走了 看这篇博客底下的评论笑死我了 真香警告!

不开心的时候不要学习
12分钟前
0
0
知识点总结

jq如何拿到data-info的自定义属性 1.1 原生可以获取到所有属性el.attrbutes 1.2 jq的$(el).attr('属性名称') 继承的几种方式,原型链 2.1 扩展原型对象实现继承 2.2 替换原型对象实现继承 2....

litCabbage
15分钟前
0
0
python语言规范

http://zh-google-styleguide.readthedocs.io/en/latest/google-python-styleguide/python_style_rules/...

ghou-靠墙哭
19分钟前
0
0
istio 监控,遥测 (理论)

Istio提供了一种灵活的模型来强制执行授权策略并收集网格中服务的遥测。 基础架构后端旨在提供用于构建服务的支持功能。它们包括诸如访问控制系统,遥测捕获系统,配额执行系统,计费系统等之...

xiaomin0322
21分钟前
0
0
阿里资深专家面试问题收集

corejava hashcode相等的两个对象一定相等吗?equals呢?反过来相等吗? 介绍一下集合框架? hashtable,hashmap底层实现是什么?hashtable和concurrenthashmap底层实现的区别? hashmap和treemap的...

undefine
22分钟前
8
0
alpine安装软件指定安装源

linux-alpine安装软件指定安装源 一、永久修改apk下载源地址 vi etc/apk/repositories 替换成阿里源 http://mirrors.aliyun.com/alpine/v3.8/main/http://mirrors.aliyun.com/alpine/v3...

我心中有猛狗
23分钟前
0
0
Centos7通过yum安装nginx

添加源地址(直接install可能不是最新版本的) sudo rpm -Uvh http://nginx.org/packages/centos/7/noarch/RPMS/nginx-release-centos-7-0.el7.ngx.noarch.rpm 安装 sudo yum install -y ng......

iplusx
25分钟前
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部