文档章节

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

猪刚烈
 猪刚烈
发布于 2014/09/24 13:55
字数 1076
阅读 277
收藏 5

        在许多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是否过期,如果过期,给出提示,并跳转到登录界面。

    拦截器配置 

web.xml配置

<init-param>
            <description>Spring MVC配置文件</description>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:controller.xml</param-value>
        </init-param>
controller.xml配置

<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方式交互时,前台需做如下相应处理:

//控制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是否存在来禁止登录。但是之前登录的若关闭了标签页,则在这个浏览器上的其他标签页则再也无法登录了。所以这个做法也有问题。




本文转载自:http://blog.csdn.net/jrn1012/article/details/25781319

猪刚烈
粉丝 22
博文 708
码字总数 110
作品 1
海淀
程序员
私信 提问
加载中

评论(1)

哎呦-又忘了
哎呦-又忘了
同一浏览器登录 在登录的时候 判断是否登录 若没有登录 让其登录,否则提示已经登录。可否



spring mvc session超时,处理ajax请求

做web开发时,当session超时时,如果不是ajax请求,很简单就能实现跳到指定的页面。但是ajax请求就会有问题。session超时的时候,点击到ajax请求就会弹出一些页面源码文件。 首先建了个拦截器...

zkool
2014/09/30
7.8K
0
SpringMVC拦截器(实现登录验证拦截器)

SpringMVC拦截器(实现登录验证拦截器) lenglingx的个人页面2017-11-281 阅读 springmvc登录验证 本例实现登陆时的验证拦截,采用SpringMVC拦截器来实现 核心代码 首先是index.jsp,显示链接...

lenglingx的个人页面
2017/11/28
0
0
Spring MVC通过注解拦截非登录,注解获取登录信息

我们在使用Spring MVC开发API的时候,总会遇到这样的情况,就是一些API需要登录权限才能访问,而且有些API是无需登录权限,希望能够以更加简单的方式管理这种差异,如果已经登录的信息也希望...

ImWiki
05/22
0
0
SpringMVC使用Interceptor拦截器

SpringMVC 中的Interceptor 拦截器也是相当重要和相当有用的,它的主要作用是拦截用户的请求并进行相应的处理。比如通过它来进行权限验证,或者是来判断用户是否登陆,或者是像12306 那样子判...

Rickxue
2016/01/18
165
0
Spring AOP 日志拦截器的事务管理

如果要在方法执行前或后或抛出异常后加上一个自己的拦截器,或者一个环绕拦截器,在拦截器中执行一些操作,比如执行一些数据库操作,记录一些信 息,这些操作通过调用一个服务类的方法来执行...

哲别0
2018/05/18
479
0

没有更多内容

加载失败,请刷新页面

加载更多

一起来学Java8(四)——复合Lambda

在一起来学Java8(二)——Lambda表达式中我们学习了Lambda表达式的基本用法,现在来了解下复合Lambda。 Lambda表达式的的书写离不开函数式接口,复合Lambda的意思是在使用Lambda表达式实现函...

猿敲月下码
18分钟前
6
0
debian10使用putty配置交换机console口

前言:Linux的推广普及,需要配合解决实际应用方能有成效! 最近强迫自己用linux进行实际工作,过程很痛苦,还好通过网络一一解决,感谢各位无私网友博客的帮助! 系统:debian10 桌面:xfc...

W_Lu
49分钟前
10
0
aelf Enterprise 0.8.0 beta有奖公测,“Bug奖金计划”重磅开启

2019年9月30日,aelf Enterprise 0.8.0 beta版正式发布。aelf Enterprise 0.8.0 beta是一个完备的区块链系统, 包含完备的区块链系统、开发套件、开发文档、以及配套的基础应用和基础服务。 ...

AELF开发者社区
50分钟前
8
0
oracle 初始化数据库脚本

create user lpf identified by 123456; create tablespace lpf_ts_cms datafile '/opt/app/oracle/product/11.2.0/lpf.dbf' size 200M; alter user lpf default tablespace lpf_ts_cms; sel......

internetafei
55分钟前
8
0
深入了解Redis底层数据结构

说明 说到Redis的数据结构,我们大概会很快想到Redis的5种常见数据结构:字符串(String)、列表(List)、散列(Hash)、集合(Set)、有序集合(Sorted Set),以及他们的特点和运用场景。不过它们是...

TurboSanil
55分钟前
7
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部