如何做到同一个账号同一时段只能登录一个

在许多web项目中,需要禁止用户重复登录。一般来说有两种做法:

         一是在用户表中维护一个字段isOnLine(是否在线),用户登录时,设定值为true,用户退出时设定为false,在重复登录时,检索到该字段为true时,禁止用户登录。这种方法有明显的漏洞,及用户在非正常情况退出(关闭浏览器、关机等)是,该字段值一直为true,会导致用户无法登录。

          而另一种比较通用的做法是使用session监听,重复登录后,强制之前登录的session过期,从而踢出了该用户。具体做法是:使用监听器维护服务器上缓存的sessionMap,该map是以<session.getId(),session>的键值对,在登录后,使用userid替换session.getId(),从而使得sessionMap中维护的是<userid, session>的键值对。后续该帐号重复登录时,检索到已有该帐号session则强制它过期。

   

1、web.xml中配置session监听

[html]  view plain  copy
 
  在CODE上查看代码片 派生到我的代码片
  1. <listener>  
  2.         <listener-class>com.cnpc.framework.listener.SessionListener</listener-class>          
  3. </listener>  

2、session监听SessionListener类

[html]  view plain  copy
 
  在CODE上查看代码片 派生到我的代码片
  1. package com.cnpc.framework.listener;  
  2.   
  3. import javax.servlet.http.HttpSessionEvent;  
  4. import javax.servlet.http.HttpSessionListener;  
  5.   
  6. import com.cnpc.framework.utils.SessionContext;  
  7.   
  8. public class SessionListener implements HttpSessionListener {  
  9.     public  static SessionContext sessionContext=SessionContext.getInstance();  
  10.    
  11.     public void sessionCreated(HttpSessionEvent httpSessionEvent) {  
  12.         sessionContext.AddSession(httpSessionEvent.getSession());  
  13.     }  
  14.   
  15.     public void sessionDestroyed(HttpSessionEvent httpSessionEvent) {  
  16.         sessionContext.DelSession(httpSessionEvent.getSession());  
  17.     }  
  18. }  
SessionContex类(使用单例模式)

[html]  view plain  copy
 
  在CODE上查看代码片 派生到我的代码片
  1. package com.cnpc.framework.utils;  
  2.   
  3. import java.util.HashMap;  
  4.   
  5. import javax.servlet.http.HttpSession;  
  6.   
  7.   
  8. public class SessionContext {  
  9.     private static SessionContext instance;  
  10.     private HashMap<String,HttpSession> sessionMap;  
  11.    
  12.     private SessionContext() {  
  13.         sessionMap = new HashMap<String,HttpSession>();  
  14.     }  
  15.   
  16.     public static SessionContext getInstance() {  
  17.         if (instance == null) {  
  18.             instance = new SessionContext();  
  19.         }  
  20.         return instance;  
  21.     }  
  22.   
  23.     public synchronized void AddSession(HttpSession session) {  
  24.         if (session != null) {  
  25.             sessionMap.put(session.getId(), session);  
  26.         }  
  27.     }  
  28.   
  29.     public synchronized void DelSession(HttpSession session) {  
  30.         if (session != null) {  
  31.             sessionMap.remove(session.getId());  
  32.             if(session.getAttribute("userid")!=null){  
  33.                 sessionMap.remove(session.getAttribute("userid").toString());  
  34.                 //session.invalidate();   
  35.             }  
  36.         }  
  37.     }  
  38.   
  39.     public synchronized HttpSession getSession(String session_id) {  
  40.         if (session_id == null) return null;  
  41.         return (HttpSession) sessionMap.get(session_id);  
  42.     }  
  43.   
  44.     public HashMap getSessionMap() {  
  45.         return sessionMap;  
  46.     }  
  47.   
  48.     public void setMymap(HashMap sessionMap) {  
  49.         this.sessionMap = sessionMap;  
  50.     }  
  51.   
  52. }  

3、用户登录成功后,更新session Map,如重复登录,强制之前session过期

[html]  view plain  copy
 
  在CODE上查看代码片 派生到我的代码片
  1. public void sessionHandlerByCacheMap(HttpSession session){  
  2.         String userid=session.getAttribute("userid").toString();  
  3.         if(SessionListener.sessionContext.getSessionMap().get(userid)!=null){  
  4.             HttpSession userSession=(HttpSession)SessionListener.sessionContext.getSessionMap().get(userid);  
  5.             //注销在线用户  
  6.             userSession.invalidate();             
  7.             SessionListener.sessionContext.getSessionMap().remove(userid);  
  8.             //清除在线用户后,更新map,替换map sessionid  
  9.             SessionListener.sessionContext.getSessionMap().remove(session.getId());   
  10.             SessionListener.sessionContext.getSessionMap().put(userid,session);   
  11.         }  
  12.         else  
  13.         {  
  14.             // 根据当前sessionid 取session对象。 更新map key=用户名 value=session对象 删除map  
  15.                 SessionListener.sessionContext.getSessionMap().get(session.getId());  
  16.             SessionListener.sessionContext.getSessionMap().put(userid,SessionListener.sessionContext.getSessionMap().get(session.getId()));  
  17.             SessionListener.sessionContext.getSessionMap().remove(session.getId());  
  18.         }  
  19.     }  

4、spring MVC拦截器校验session是否过期,如果过期,给出提示,并跳转到登录界面。

    拦截器配置  

web.xml配置

[html]  view plain  copy
 
  在CODE上查看代码片 派生到我的代码片
  1.       <init-param>  
  2.             <description>Spring MVC配置文件</description>  
  3.             <param-name>contextConfigLocation</param-name>  
  4.             <param-value>classpath:controller.xml</param-value>  
  5.         </init-param>  
controller.xml配置

[html]  view plain  copy
 
  在CODE上查看代码片 派生到我的代码片
  1. <mvc:interceptors>     
  2.         <bean class="com.cnpc.framework.interceptor.AuthInterceptor" />    
  3.     </mvc:interceptors>    

拦截器authInterceptor

[html]  view plain  copy
 
  在CODE上查看代码片 派生到我的代码片
  1. package com.cnpc.framework.interceptor;  
  2.   
  3. import java.io.PrintWriter;  
  4. import java.util.Map;  
  5.   
  6. import javax.annotation.Resource;  
  7. import javax.servlet.http.HttpServletRequest;  
  8. import javax.servlet.http.HttpServletResponse;  
  9.   
  10. import org.springframework.stereotype.Component;  
  11. import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;  
  12.   
  13. import com.cnpc.framework.common.SessionContainer;  
  14.   
  15. @Component("SpringMVCInterceptor")  
  16. public class AuthInterceptor extends HandlerInterceptorAdapter {      
  17.       
  18.     @Override  
  19.     public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {  
  20.         request.setCharacterEncoding("UTF-8");  
  21.         response.setCharacterEncoding("UTF-8");   
  22.         response.setContentType("text/html;charset=UTF-8");  
  23.    
  24.         //过滤登录、退出访问  
  25.         String[] noFilters = new String[] { "/auth/login", "/auth/logout" };  
  26.         String uri = request.getRequestURI();  
  27.   
  28.         boolean beFilter = true;  
  29.         for (String s : noFilters) {  
  30.             if (uri.indexOf(s) != -1) {  
  31.                 beFilter = false;  
  32.                 break;  
  33.             }  
  34.         }  
  35.         SessionContainer sessionContainer = (SessionContainer) request.getSession().getAttribute("SessionContainer");  
  36.         if (beFilter) {  
  37.             if (null == sessionContainer) {  
  38.                 //ajax方式交互  
  39.                 if (request.getHeader("x-requested-with") != null  
  40.                         && request.getHeader("x-requested-with").equalsIgnoreCase("XMLHttpRequest"))// 如果是ajax请求响应头会有,x-requested-with;  
  41.                 {                     
  42.                     response.setHeader("sessionstatus", "timeout");// 在响应头设置session状态  
  43.                     return false;  
  44.                 }  
  45.                 // 未登录  
  46.                 PrintWriter out = response.getWriter();  
  47.                 StringBuilder builder = new StringBuilder();  
  48.                 builder.append("<script type=\"text/javascript\" charset=\"UTF-8\">");  
  49.                 builder.append("alert(\"页面过期,请重新登录\");");  
  50.                 builder.append("window.top.location.href='/auth/logout';");  
  51.                 builder.append("</script>");  
  52.                 out.print(builder.toString());  
  53.                 out.close();  
  54.                 return false;  
  55.             } else {                      
  56.                 // 添加系统日志  
  57.                 // -----------------------------------  
  58.                 // -----------------------------------  
  59.             }  
  60.         }  
  61.         Map paramsMap = request.getParameterMap();  
  62.         return super.preHandle(request, response, handler);  
  63.     }  
  64. }  

以上Sprring MVC拦截器在同服务器以ajax方式交互时,前台需做如下相应处理:

[html]  view plain  copy
 
  在CODE上查看代码片 派生到我的代码片
  1. //控制ajax请求,session超时处理页面跳转  
  2.  $.ajaxSetup({     
  3.        contentType:"application/x-www-form-urlencoded;charset=utf-8",     
  4.        complete:function(XMLHttpRequest,textStatus){     
  5.              var sessionstatus=XMLHttpRequest.getResponseHeader("sessionstatus"); // 通过XMLHttpRequest取得响应头,sessionstatus,  
  6.                if(sessionstatus=="timeout"){     
  7.                      // 如果超时就处理 ,指定要跳转的页面     
  8.                      alert("页面过期,请重新登录");   
  9.                      window.top.location.href="/auth/logout";  
  10.                     }    
  11.                   }       
  12.                }   
  13.           );     

           以上方式完成了禁止用户重复登录的功能,并将踢出之前登录的帐号,这在不同的浏览器中使用没有问题。但是因为在同一个浏览器中,多个标签页(Tab页)是共享session的,在同一个浏览器中并不会创建一个新的session。所以同一个浏览器还是可以重复登录的,目前还没有什么很好解决办法。本来想如果重复登录,则通过校验session是否存在来禁止登录。但是之前登录的若关闭了标签页,则在这个浏览器上的其他标签页则再也无法登录了。所以这个做法也有问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值