开发者社区> 消失er> 正文
阿里云
为了无法计算的价值
打开APP
阿里云APP内打开

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

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

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

img_4801b1ff7013a361ec4933e2df95f852.png
image

从上面可以看出:同一个浏览器对于不同的账号,登录时会产生相同的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:

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

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

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
pm2 管理nodejs集群,同时使用redis共享session
通过pm2来实现nodejs应用的集群,不过我之前没做session共享,导致.. 登录不上啊 啊啊啊,无奈,又重新对redis进行了集成。
133 0
systemd 对你的进程数限制横插一脚啦
原文:https://blog.apporc.org/2016/05/systemd-%E5%AF%B9%E4%BD%A0%E7%9A%84%E8%BF%9B%E7%A8%8B%E6%95%B0%E9%99%90%E5%88%B6%E6%A8%AA%E6%8F%92%E4%B8%80%E8%84%9A%E5%95%A6/ 以往在执行海量进程并发的时候,看到这错:”fork:retry:no child process”, 通常就想到 ulimit 了。
1198 0
Javascript中如何获取统一管理的Java提示语
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/catoop/article/details/50630121 项目开发中,各个页面,各个业务操作都会使用提示语。
929 0
彻底理解PHP的SESSION机制
1.session.save_handler = files     * 1. session_start()          1. session_start()是session机制的开始,它有一定概率开启垃圾回收,因为session是存放在文件中, PHP自身的垃圾回收是无效的,SESSION的回收是要删文件的,这个概率是根据php.ini的配置决定的, 但是有的系统是 se
1340 0
+关注
消失er
此人小小程序员一枚~ 写点技术、写点生活。 我的简书 https://www.jianshu.com/u/dd8907cc9fa5 CSDN https://my.csdn.net/ljheee GitHub https://github.com/ljheee
59
文章
19
问答
文章排行榜
最热
最新
相关电子书
更多
低代码开发师(初级)实战教程
立即下载
阿里巴巴DevOps 最佳实践手册
立即下载
冬季实战营第三期:MySQL数据库进阶实战
立即下载