`
齐晓威_518
  • 浏览: 607704 次
  • 性别: Icon_minigender_1
  • 来自: 郑州
社区版块
存档分类
最新评论

同一账号不能多地登录(限制同一账号同一时刻只能一个用户登录使用,向QQ一样)

 
阅读更多
解决四个问题:
1. 实现在线用户列表
2. 当用户在异地登录后,使前一次登录自动退出
3. 关闭浏览器,session失效,该session对应当前登录用户将会从在线列表移除,
4. 用户正常退出,session失效,该session对应当前登录用户将会从在线列表移除,


实现方法: 
用户登录时,会创建一个session,用于保存用户信息。将所有用户登录时的session值与ID存入ServletContext中,显示在线列表的时候,就从ServletContext中取得用户登录的session值,从中取得用户信息。
(限制同一账号同一时刻只能一个用户登录使用向QQ一样,不能多地登录
情况一:没有登录,则将用户登录信息放置于用户在线容器列表
情况二:已登录,则判断是否异地重复登录,重复登录则踢出上一登录,将其session失效,
将最新的登录会话放置于用户在线列表中。)

1. 登录:
先从ServletContext中取出存放用户登录的session 相关信息,检查这个列表,如果已经存在相同的登录信息,则说明用户之前已经登录过,移除前面一条记录。
再把此次登录的信息加入到ServletContext中。

2.监听:
实现SessionListener类,当session失效的时候,从ServletContext中移除相应记录。

3.过滤:
过滤所有页面,sesison失效后转向登录页面。但是要实现用户二次登录后强制先前的登录失效,需要在这里控制。
登录时存入的是session值和session ID,用户二次登录时移除了前线记录,存入的session值是相同的,但是ID却不同。
当第一次登录的页面请求的时候,在这里检查ServletContext中是否存在当前的session值与ID记录。如果没有就销毁这个session。


参考代码:
存放的时候有很多中方法,我选择的是将session值与ID先存入一个List,在将这个List存到ServletContext中。
即将用户登录信息放置于用户在线容器列表,已登录,则判断是否重复登录,重复登录则踢出上一登录,将其session失效,
将最新的登录放置于用户在线列表中。

============================1. 登录部分============================
一、Struts中的Action登录校验方法
  String forwardAction = LOGIN_ACTION;
  boolean checkFlag=false;//是否验证通过
  String userId = WebUtils.getParameter(request,"userId", null);
  String userPassword = WebUtils.getParameter(request,"userPassword", null);
  if (StringUtils.isNotEmpty(userId) && StringUtils.isNotEmpty(userPassword)) {
   AppUser appUser = this.userService.getAppUser(userId);
   if (appUser != null) {
    String pass = appUser.getPassWord();
    if(appUser.getIsLock()==1){
     request.setAttribute("userLockError", "抱歉,该用户已经被锁定!");
    } else if (appUser.getUserFlag()==2) {
     request.setAttribute("userLockError", "抱歉,该用户已经被注销!");
    } else if (userPassword.equals(pass)) {
       checkFlag=true;
        /**
         * 第二:判断用户用户是否重复登录
         * 限制同一账号同一时刻只能一个用户登录使用向QQ一样,不能多地登录
         * 情况一:没有登录,则将用户登录信息放置于用户在线容器列表
         * 情况二:已登录,则判断是否重复登录,重复登录则踢出上一登录,将其session失效,
         * 将最新的登录放置于用户在线列表中。
         */    
        if(SessionManager.getSingleUserLoginManage(request,appUser)){    
         return new ActionForward("/" +forwardAction);      
        }else{
         forwardAction = MAIN_ACTION;
        }        
    } else {
     request.setAttribute("passError", "密码错误");
    }
   } else {
    request.setAttribute("userError", "用户名错误");
   }
  }
  if(checkFlag){
   response.sendRedirect(forwardAction);
   return null;
  }else{
   return new ActionForward("/" +forwardAction);
  }

二、校验是否重复登录公共方法
/**
  * 限制同一账号同一时刻只能一个用户登录使用向QQ一样,不能多地登录
  * @Description:用户在线管理控制类
  * 判断用户用户是否重复登录
  * 情况一:没有登录,则将用户登录信息放置于用户在线容器列表
  * 情况二:已登录,则判断是否重复登录,重复登录则踢出上一登录,将其session失效,
  * 将最新的登录放置于用户在线列表中。
  * @param appUser
  * @ReturnType boolean
  * @author: QIXIAOWEI
  * @Created xxxxxxxxxxxxx
  */
public static boolean getSingleUserLoginManage(HttpServletRequest request,AppUser appUser){
  String operationFlag ="N";//用户是否被踢除,Y表示踢除成功,N踢除失败
  boolean forwardFlag=false;//踢除重复登录用户,被踢除用户是否转向登录页面
  int interval = PropertiesBean.getProperty(PARAM_SESSION_TIMEOUT, DEFUALT_SESSION_TIMEOUT);
  HttpSession session = request.getSession(true);
  //声明application系统公有数据
     ServletContext application = (ServletContext)session.getServletContext();    
     //用户登录信息
     session.setAttribute("loginCurrUser",appUser);    
     session.setAttribute("id", session.getId());
     //设置session超时时间
     session.setMaxInactiveInterval(interval);
     //session生成时间的值
     session.setAttribute("loginTime",session.getCreationTime());      
     /**
      * 第二:判断用户用户是否重复登录
      * 情况一:没有登录,则将用户登录信息放置于用户在线容器列表
      * 情况二:已登录,则判断是否重复登录,重复登录则踢出上一登录,将其session失效,
      * 将最新的登录放置于用户在线列表中。
      */
     List onlineUserList = (List) application.getAttribute("onlineUserList");
     if (onlineUserList ==null || onlineUserList.size()==0) {
      /**判断用户在线容器列表是否存在,没有则创建一个并放入ServletContext **/
         onlineUserList = new ArrayList();    
         application.setAttribute("onlineUserList", onlineUserList);
         /**将登录用户会话信息,并设置到ServletContext中用户在线容器List中**/
         onlineUserList.add(session);
     }else {
      /**检查用户是否重复登录,如果重复登录,则踢出上一次登录,保留本次登录-START**/
      if(onlineUserList!=null && onlineUserList.size()>0){
       /**遍历用户信息List容器-START**/
       for(int i=0;i<onlineUserList.size();i++){    
        /**分别取出在线列表中的每一个登录会话信息**/
        HttpSession obj = (HttpSession)onlineUserList.get(i);
        /**
         * 判断用户是否重复登录
         * 如果存在就返回当前的会话。如果参数是false,
         * 那么在request的当前会话不存在的时候就返回null
         ***/
        boolean clearFlag=false;//如果重复登录,踢除上一次,处理用户刷新
        /**取出application对象中放置的会话信息集合某个属性,与当前新登录会话信息属性进行比较-判断用户是否登录-START**/
        if(request.getSession(false)!=null && obj!=null){
         if(session.getAttribute("loginCurrUser")!=null && obj.getAttribute("loginCurrUser")!=null){    
          AppUser AppUserOne=(AppUser)obj.getAttribute("loginCurrUser");
          AppUser AppUserTwo=(AppUser)session.getAttribute("loginCurrUser");
          if((AppUserOne!=null && AppUserTwo!=null) && ((AppUserOne.getUserId()).equals(AppUserTwo.getUserId()))){    
           if(obj.getAttribute("loginTime")!=null && session.getAttribute("loginTime")!=null){
            /**在线列表中该用户上次session生成时间**/
            long obj_time = Long.parseLong(obj.getAttribute("loginTime").toString());    
            /**当前该用户新会话session生成时间的值**/
            long session_time = Long.parseLong(session.getAttribute("loginTime").toString());        
           
            /**session生成时间转换成日期-START**/
            Date firstDate=new Date(obj_time);//
            Date secondDate=new Date(session_time);
            //前面的MM一定要大写,表示月份,后面的mm小写,表示分钟
            //SimpleDateFormat  firstFormat=new SimpleDateFormat("yyyy-MM-dd HH-mm-ss");
            //SimpleDateFormat  secondFormat=new SimpleDateFormat("yyyy-MM-dd HH-mm-ss");
            /**session生成时间转换成日期-END**/            
            /**
             * 特殊情况控制,用户同一浏览器登录-,虽然登录时间不同
             * 但为同一用户,不再向用户在线列表中增加值
             **************************************************
             * 如果非要控制同一账户同一系统,同一浏览也不允许登录,则需要重新
             * 执行下面踢除操作,并重新执行第一步,重新生成会话
             **************************************************
             ***/
            clearFlag=true;
           
            /**
             * 在满足全局对象‘用户对象标识值’与新登录用户标识值相同的情况下
             * 将application对象中的会话属性(session生成时间的值)与新的会话属性进行比较
             * 如果旧的会话生成时间小于新的会话生成时间,则表示该用户已经登录
             ******************************************************
             *如果成功页面用的是转发不是重定向,该句需要放开,涉及到刷新
             * -START**/
            //if(obj_time<session_time && DateUtils.isBefore(firstDate, secondDate)){
             /**将新的会话放置于集合中**/
             onlineUserList.add(session);
             obj.invalidate();//销毁原来的session    
             operationFlag="Y";    
             break;
            //}
           }
          }
         }
        } 
        /**没有重复的,即正常登录,将登录会话放于集合中,用于登录后页面刷新**/
        if(!clearFlag){    
         onlineUserList.add(session);
         break;
        }
        /**取出application对象中放置的会话信息集合某个属性,与当前新登录会话信息属性进行比较-判断用户是否登录-END**/
       }
       /**遍历用户信息List容器-END**/
      }        
      /**检查用户是否重复登录,如果重复登录,则踢出上一次登录,保留本次登录-END**/    
     }        
    
     if(operationFlag.equals("Y")){        
      if(session.getAttribute("loginCurrUser")==null){    
       //("【登录主界面-封装类】踢除会话后转向页面");    
       forwardFlag=true;
      }  
     }    
     return forwardFlag;
}


/**
  * 用户退出.
  */
  XXX appUser = SessionManager.getLoginUser();
  if (appUser != null) {
   request.getSession().invalidate();//销毁session
   log.info("用户登出系统,登出用户为:" + appUser.getUserId());
  }
  response.sendRedirect(forwardAction);
  return null;

============================2. 监听部分============================
/**
* 任何一个Session被创建或者销毁时,都会通知OnlineUserListener 这个类
* @Description: Session使用HttpSessionListener接口监听Session的创建和失效
* @ClassName OnlineUserListener
*/
public class OnlineUserListener implements HttpSessionListener{
    protected final Logger log = Logger.getLogger(getClass());

/**
  * 当一个浏览器第一次访问网站的时候,J2EE应用服务器会新建一个HttpSession对象 ,
  * 并触发 HttpSession创建事件 ,如果注册了HttpSessionListener事件监听器,
  * 则会调用HttpSessionListener事件监听器的sessionCreated方法
  */
public void sessionCreated(HttpSessionEvent event) {
   // 当session建立时触发  
  log.info("【监听器提醒】:当session建立时触发");
}
   
/**
  * 销毁Session
  * 这个浏览器访问结束超时的时候,J2EE应用服务器会销毁相应的HttpSession对象,
  * 触发 HttpSession销毁事件,同时调用所注册HttpSessionListener
  * 事件监听器的sessionDestroyed方法
  */
public void sessionDestroyed(HttpSessionEvent event) { 
  log.info("【监听器提醒】:当session销毁时触发"); 
  HttpSession session = event.getSession(); 
  ServletContext application = session.getServletContext();   
  //从ServletContext中取得在线列表用户信息的容器
  List onlineUserList = (List) application.getAttribute("onlineUserList");
  onlineUserList.remove(session);   
  log.info("【监听器提醒】:获取上次与服务器交互时间,time: " + session.getLastAccessedTime());
}
}

============================3. 过滤部分============================
   if(request.getSession().getAttribute("loginCurrUser")==null){
    log.error("没有登录系统,请登录系统!");
    String url = request.getRequestURL().toString();
    request.setAttribute("errorAction", url.substring(0, url.lastIndexOf("/")));    
    return mapping.findForward("error");
   }
===========================4. JSP页面部分============================
<%if(session.getAttribute("loginCurrUser")==null){%>
<script type="text/javascript">
var errorAction = '<c:out value="${errorAction}"/>';
alert("你的账号已在异地登录,你被强制下线!");
window.top.location.href='index.do?action=LoginUser';
</script>
<%}%>

首先介绍一下Cookie、Application和Session对象
1.Cookie:
Cookie 是保存到客户端的的一个文本文件,与特定的用户相关,以键---值对的形式存放
创建Cookie的方法:Cookie cookie = new Cookie(name ,value);其中name和value 都是String的类型,可以用setXXX getXXX方法设置Cookie的属性和获得属性,然后再利用HttpServletResponse的方法
addCookie(Cookie)将它设置到客户端。
利用HttpServletRequest的getCookies()方法读取客户端的所有Cookie,返回一个Cookie数组
2.Session
存储Session的两种方法:将Session写到浏览器的Cookie中  
通过URL的重写
建立一个Session的方法,HttpSession mySession = request.getSession(true);
Session是写在服务器的文件,mySession 具有相应的方法设置Session的属性(比如存活时间)
通过response.encodeURL() 转码,将URL后面加上SessionId,如果浏览器没有禁用Cookie的话,就将Session写到Cookie中
3.Application
用于保存整个WebApplication的生命周期内部都可以访问的数据,即保存在服务器
在API中表现为ServletContext,通过HttpServlet的getServletContext方法可以拿到,通过ServletContext的get/setAttribute的方法取得相应的方法和设置相关的属性
Session与Application 都是通过键---值段的保存,setAttribute(name,value); name是String类型,value
是任何类型。
利用Application的方法实现网站的计数:
public class TestServletContext extends HttpServlet {
protected void doGet(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException {
  ServletContext application = this.getServletContext();
  PrintWriter pw = resp.getWriter();
  Integer accessCount = (Integer)application.getAttribute("accessCount");
  if(accessCount == null) {
       accessCount = new Integer(0);  
  } else accessCount = new Integer(accessCount.intValue() + 1); 
      application.setAttribute("accessCount", accessCount);
      pw.println("accessCount:" + accessCount.intValue()); 
}
}
分享到:
评论
1 楼 JAVA_CLASSm 2016-10-31  
你好,请问这个还有源码吗?我刚开始接触这个,想要源码学习一下...

相关推荐

    安装NumPy教程-详细版

    附件是安装NumPy教程_详细版,文件绿色安全,请大家放心下载,仅供交流学习使用,无任何商业目的!

    语音端点检测及其在Matlab中的实现.zip

    语音端点检测及其在Matlab中的实现.zip

    C#文档打印程序Demo

    使用C#完成一般文档的打印,带有页眉,页脚文档打印,表格打印,打印预览等

    DirectX修复工具-4-194985.zip

    directx修复工具 DirectX修复工具(DirectX repair)是系统DirectX组件修复工具,DirectX修复工具主要是用于检测当前系统的DirectX状态,若发现异常情况就可以马上进行修复,非常快捷,使用效果也非常好。

    Python手动实现人脸识别算法

    人脸识别的主要算法 其核心算法是 欧式距离算法使用该算法计算两张脸的面部特征差异,一般在0.6 以下都可以被认为是同一张脸 人脸识别的主要步骤 1 获得人脸图片 2 将人脸图片转为128D的矩阵(这个也就是人脸特征的一种数字化表现) 3 保存人脸128D的特征到文件中 4 获取其他人脸转为128D特征通过欧式距离算法与我们保存的特征对比,如果差距在0.6以下就说明两张脸差距比较小

    全国大学生信息安全竞赛知识问答-CISCN 题库.zip

    ciscn 全国大学生信息安全竞赛知识问答-CISCN 题库.zip

    JAVA+SQL离散数学题库管理系统(源代码+LW+外文翻译).zip

    JAVA+SQL离散数学题库管理系统(源代码+LW+外文翻译)JAVA+SQL离散数学题库管理系统(源代码+LW+外文翻译)JAVA+SQL离散数学题库管理系统(源代码+LW+外文翻译)JAVA+SQL离散数学题库管理系统(源代码+LW+外文翻译)JAVA+SQL离散数学题库管理系统(源代码+LW+外文翻译)JAVA+SQL离散数学题库管理系统(源代码+LW+外文翻译)JAVA+SQL离散数学题库管理系统(源代码+LW+外文翻译)JAVA+SQL离散数学题库管理系统(源代码+LW+外文翻译)JAVA+SQL离散数学题库管理系统(源代码+LW+外文翻译)JAVA+SQL离散数学题库管理系统(源代码+LW+外文翻译)JAVA+SQL离散数学题库管理系统(源代码+LW+外文翻译)JAVA+SQL离散数学题库管理系统(源代码+LW+外文翻译)JAVA+SQL离散数学题库管理系统(源代码+LW+外文翻译)JAVA+SQL离散数学题库管理系统(源代码+LW+外文翻译)

    strcmp函数应用.zip

    strcmp函数应用.zip

    蓝桥杯单片机第十一届国赛设计题试做

    蓝桥杯单片机第十一届国赛设计题试做

    基于MATLAB的pca人脸识别.zip

    基于MATLAB的pca人脸识别.zip

    520.html

    520.html

    JAVA在线考试管理系统(源代码+LW+开题报告+外文翻译+英文文献+答辩PPT).zip

    JAVA在线考试管理系统(源代码+LW+开题报告+外文翻译+英文文献+答辩PPT)

    STR710的定时器编程C语言例子,开发环境为IAR EWARM。.zip

    STR710的定时器编程C语言例子,开发环境为IAR EWARM。.zip

    基于物品的协同过滤推荐算法(Python).zip

    协同过滤算法(Collaborative Filtering)是一种经典的推荐算法,其基本原理是“协同大家的反馈、评价和意见,一起对海量的信息进行过滤,从中筛选出用户可能感兴趣的信息”。它主要依赖于用户和物品之间的行为关系进行推荐。 协同过滤算法主要分为两类: 基于物品的协同过滤算法:给用户推荐与他之前喜欢的物品相似的物品。 基于用户的协同过滤算法:给用户推荐与他兴趣相似的用户喜欢的物品。 协同过滤算法的优点包括: 无需事先对商品或用户进行分类或标注,适用于各种类型的数据。 算法简单易懂,容易实现和部署。 推荐结果准确性较高,能够为用户提供个性化的推荐服务。 然而,协同过滤算法也存在一些缺点: 对数据量和数据质量要求较高,需要大量的历史数据和较高的数据质量。 容易受到“冷启动”问题的影响,即对新用户或新商品的推荐效果较差。 存在“同质化”问题,即推荐结果容易出现重复或相似的情况。 协同过滤算法在多个场景中有广泛的应用,如电商推荐系统、社交网络推荐和视频推荐系统等。在这些场景中,协同过滤算法可以根据用户的历史行为数据,推荐与用户兴趣相似的商品、用户或内容,从而提高用户的购买转化率、活跃度和社交体验。 未来,协同过滤算法的发展方向可能是结合其他推荐算法形成混合推荐系统,以充分发挥各算法的优势。

    JAVA文件传输(lw+源代码).zip

    FTP(File Transfer Protocol)是文件传输协议的简称。 FTP的主要作用,就是让用户连接上一个远程计算机(这些计算机上运行着FTP服务器程序)查看远程计算机有哪些文件,然后把文件从远程计算机上拷到本地计算机,或把本地计算机的文件送到远程计算机去。 目前FTP服务器软件都为国外作品,例如Server_U、IIS,国内成熟的FTP服务器软件很少,有一些如(Crob FTP Server),但从功能上看来远不能和那些流行的服务器软件媲美。

    python项目源码-深度学习tensorflow的滚动轴承故障诊断方法源码(高分大作业).rar

    本项目基于深度学习TensorFlow框架,针对滚动轴承故障诊断方法进行研究。项目采用了卷积神经网络(CNN)对轴承振动信号进行特征提取和分类,实现了对滚动轴承不同故障类型的自动诊断。 在技术实现上,项目利用TensorFlow搭建了一个高效的CNN模型,通过多层卷积、池化操作以及全连接层,自动学习轴承振动信号中的故障特征。同时,采用交叉熵损失函数优化模型参数,提高故障识别率。此外,项目还集成了数据预处理、模型训练、测试评估等功能模块,方便用户快速上手并进行实验研究。 经过运行测试,该项目代码运行稳定,诊断效果良好,可广泛应用于滚动轴承故障诊断领域。对于计算机相关专业的在校学生、老师或企业员工来说,该项目是一份难得的高分大作业资源,同时也是小白学习和实际项目借鉴的优秀参考资料。请放心下载使用,为您的学习和工作提供帮助!

    超详细的SpringBoot框架入门教程 Spring Boot框架快速入门教程以大量示例讲解了Spring Boot在各类情境

    超详细的SpringBoot框架入门教程 Spring Boot框架快速入门教程以大量示例讲解了Spring Boot在各类情境中的应用,让大家可以跟着老师的思维和代码快速理解并掌握。适用于Java 开发人员,尤其是初学Spring Boot的人员和需要从传统 Spring 转向 Spring Boot 开发的技术人员。 下边是动力节点的SpringBoot教程非常适合初学入门,讲的非常详细,而且全程无废话!

    毕业设计[主机域名]ISPConfig 3.0.1.3_ispconfig3-codepub.zip

    毕业设计[主机域名]ISPConfig 3.0.1.3_ispconfig3-codepub.zip

    matlab开发-用交叉熵优化多变量宏观模型随机多极值优化.zip

    matlab开发-用交叉熵优化多变量宏观模型随机多极值优化.zip

    矩阵特征值的计算方法.zip

    矩阵特征值的计算方法.zip

Global site tag (gtag.js) - Google Analytics