同一账号不能多地登录(限制同一账号同一时刻只能一个用户登

解决四个问题: 
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());  

}

  • 2
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要实现多用户同时登录同一网站且同一账号不能同一时间重复登录的效果,你可以考虑以下步骤和策略: 1. 使用Session或Token机制:当用户成功登录后,为其生成一个唯一的Session ID或Token,并将其存储在服务器端的缓存中(如内存缓存或分布式缓存)。每次用户发送请求时,将该Session ID或Token作为标识传递给服务器进行验证。 2. 登录时验证唯一性:在用户登录时,先检查该账号是否已经有其他用户登录。如果有其他用户已经登录,则拒绝当前用户登录请求。 3. 登录冲突处理:如果有其他用户已经登录,你可以选择直接拒绝后续登录请求,或者将之前登录用户强制下线。如果选择后者,你需要在用户登录时更新Session ID或Token,并通知之前登录用户进行重新登录。 4. 定时过期机制:为每个Session ID或Token设置过期时间,在一定时间内没有活动的情况下自动过期并清除缓存。这样可以保证即使用户没有主动退出,也能够在一段时间后自动释放账号。 5. 合理设置并发限制:为了防止恶意攻击或滥用,你可以设置并发限制限制同一账号同时登录的数量。根据需求和系统负载,可以设定一个合理的并发登录数,并在达到限制时拒绝后续登录请求。 需要注意的是,以上策略只是一种常见的实现方式,具体实现还需根据你的项目需求和技术栈进行调整和优化。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值