使用session监听+spring MVC拦截器禁止用户重复登录

http://blog.csdn.net/jrn1012/article/details/25781319
在许多web项目中,需要禁止用户重复登录。一般来说有两种做法:

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

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

  
1、web.xml中配置session监听
<listener>  
        <listener-class>com.cnpc.framework.listener.SessionListener</listener-class>          
</listener>  


2、session监听SessionListener类
package com.cnpc.framework.listener;  
  
import javax.servlet.http.HttpSessionEvent;  
import javax.servlet.http.HttpSessionListener;  
  
import com.cnpc.framework.utils.SessionContext;  
  
public class SessionListener implements HttpSessionListener {  
    public  static SessionContext sessionContext=SessionContext.getInstance();  
   
    public void sessionCreated(HttpSessionEvent httpSessionEvent) {  
        sessionContext.AddSession(httpSessionEvent.getSession());  
    }  
  
    public void sessionDestroyed(HttpSessionEvent httpSessionEvent) {  
        sessionContext.DelSession(httpSessionEvent.getSession());  
    }  
}  

SessionContex类(使用单例模式)
package com.cnpc.framework.utils;  
  
import java.util.HashMap;  
  
import javax.servlet.http.HttpSession;  
  
  
public class SessionContext {  
    private static SessionContext instance;  
    private HashMap<String,HttpSession> sessionMap;  
   
    private SessionContext() {  
        sessionMap = new HashMap<String,HttpSession>();  
    }  
  
    public static SessionContext getInstance() {  
        if (instance == null) {  
            instance = new SessionContext();  
        }  
        return instance;  
    }  
  
    public synchronized void AddSession(HttpSession session) {  
        if (session != null) {  
            sessionMap.put(session.getId(), session);  
        }  
    }  
  
    public synchronized void DelSession(HttpSession session) {  
        if (session != null) {  
            sessionMap.remove(session.getId());  
            if(session.getAttribute("userid")!=null){  
                sessionMap.remove(session.getAttribute("userid").toString());  
                //session.invalidate();   
            }  
        }  
    }  
  
    public synchronized HttpSession getSession(String session_id) {  
        if (session_id == null) return null;  
        return (HttpSession) sessionMap.get(session_id);  
    }  
  
    public HashMap getSessionMap() {  
        return sessionMap;  
    }  
  
    public void setMymap(HashMap sessionMap) {  
        this.sessionMap = sessionMap;  
    }  
  
}  

3、用户登录成功后,更新session Map,如重复登录,强制之前session过期
public void sessionHandlerByCacheMap(HttpSession session){  
        String userid=session.getAttribute("userid").toString();  
        if(SessionListener.sessionContext.getSessionMap().get(userid)!=null){  
            HttpSession userSession=(HttpSession)SessionListener.sessionContext.getSessionMap().get(userid);  
            //注销在线用户  
            userSession.invalidate();             
            SessionListener.sessionContext.getSessionMap().remove(userid);  
            //清除在线用户后,更新map,替换map sessionid  
            SessionListener.sessionContext.getSessionMap().remove(session.getId());   
            SessionListener.sessionContext.getSessionMap().put(userid,session);   
        }  
        else  
        {  
            // 根据当前sessionid 取session对象。 更新map key=用户名 value=session对象 删除map  
                SessionListener.sessionContext.getSessionMap().get(session.getId());  
            SessionListener.sessionContext.getSessionMap().put(userid,SessionListener.sessionContext.getSessionMap().get(session.getId()));  
            SessionListener.sessionContext.getSessionMap().remove(session.getId());  
        }  
    } 


4、spring MVC拦截器校验session是否过期,如果过期,给出提示,并跳转到登录界面。
    拦截器配置  
      <init-param>  
            <description>Spring MVC配置文件</description>  
            <param-name>contextConfigLocation</param-name>  
            <param-value>classpath:controller.xml</param-value>  
        </init-param>  
controller.xml配置
[html] view plaincopy在CODE上查看代码片派生到我的代码片
<mvc:interceptors>     
        <bean class="com.cnpc.framework.interceptor.AuthInterceptor" />    
    </mvc:interceptors>  
 

拦截器authInterceptor
package com.cnpc.framework.interceptor;  
  
import java.io.PrintWriter;  
import java.util.Map;  
  
import javax.annotation.Resource;  
import javax.servlet.http.HttpServletRequest;  
import javax.servlet.http.HttpServletResponse;  
  
import org.springframework.stereotype.Component;  
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;  
  
import com.cnpc.framework.common.SessionContainer;  
  
@Component("SpringMVCInterceptor")  
public class AuthInterceptor extends HandlerInterceptorAdapter {      
      
    @Override  
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {  
        request.setCharacterEncoding("UTF-8");  
        response.setCharacterEncoding("UTF-8");   
        response.setContentType("text/html;charset=UTF-8");  
   
        //过滤登录、退出访问  
        String[] noFilters = new String[] { "/auth/login", "/auth/logout" };  
        String uri = request.getRequestURI();  
  
        boolean beFilter = true;  
        for (String s : noFilters) {  
            if (uri.indexOf(s) != -1) {  
                beFilter = false;  
                break;  
            }  
        }  
        SessionContainer sessionContainer = (SessionContainer) request.getSession().getAttribute("SessionContainer");  
        if (beFilter) {  
            if (null == sessionContainer) {  
                //ajax方式交互  
                if (request.getHeader("x-requested-with") != null  
                        && request.getHeader("x-requested-with").equalsIgnoreCase("XMLHttpRequest"))// 如果是ajax请求响应头会有,x-requested-with;  
                {                     
                    response.setHeader("sessionstatus", "timeout");// 在响应头设置session状态  
                    return false;  
                }  
                // 未登录  
                PrintWriter out = response.getWriter();  
                StringBuilder builder = new StringBuilder();  
                builder.append("<script type=\"text/javascript\" charset=\"UTF-8\">");  
                builder.append("alert(\"页面过期,请重新登录\");");  
                builder.append("window.top.location.href='/auth/logout';");  
                builder.append("</script>");  
                out.print(builder.toString());  
                out.close();  
                return false;  
            } else {                      
                // 添加系统日志  
                // -----------------------------------  
                // -----------------------------------  
            }  
        }  
        Map paramsMap = request.getParameterMap();  
        return super.preHandle(request, response, handler);  
    }  
}  

以上Sprring MVC拦截器在同服务器以ajax方式交互时,前台需做如下相应处理:
[html] view plaincopy在CODE上查看代码片派生到我的代码片
//控制ajax请求,session超时处理页面跳转  
 $.ajaxSetup({     
       contentType:"application/x-www-form-urlencoded;charset=utf-8",     
       complete:function(XMLHttpRequest,textStatus){     
             var sessionstatus=XMLHttpRequest.getResponseHeader("sessionstatus"); // 通过XMLHttpRequest取得响应头,sessionstatus,  
               if(sessionstatus=="timeout"){     
                     // 如果超时就处理 ,指定要跳转的页面     
                     alert("页面过期,请重新登录");   
                     window.top.location.href="/auth/logout";  
                    }    
                  }       
               }   
          );  
  

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

猜你喜欢

转载自panyongzheng.iteye.com/blog/2203522
今日推荐