SpringMVC拦截器实现单点登录

 更新时间:2017年11月23日 14:11:43   作者:Rava722  
这篇文章主要介绍了SpringMVC拦截器实现单点登录,简单介绍了springmvc拦截器,单点登录实现原理等相关内容,具有一定参考价值,需要的朋友可以了解下。

单点登录的功能在实际的应用场景中还是很重要的,逻辑上我们也不允许一个用户同时在进行着两个操作,下面就来了解一下SpringMVC的单点登录实现

SpringMVC的拦截器不同于Spring的拦截器,SpringMVC具有统一的入口DispatcherServlet,所有的请求都通过DispatcherServlet,所以只需要在DispatcherServlet上做文章即可,DispatcherServlet也没有代理,同时SpringMVC管理的Controller也不有代理。

1,先探究一个基本的实现原理:这个功能还是比较简单的,就是对于同一个web项目同一个时间只能有一个用户在进行操作,所以这里就涉及到一个异地登录的发现,而这里就推出两条路,1是服务器发现已登录的用户通过另一个IP再次执行了登录操作,然后主动推送一个提醒告诉第一个用户账号异地登录;2是当用户进行操作的时候发现自己的账号被挤掉了,有异地登录。关于这两个方案,第一种是比较具有实时性的但是还在探究中,下面重点说一说第二种方式。

2,实现第二种也比较简单,两个操作,一是在用户表中多加一个字段,用来存储登录用户的SessionId,因为每一个request请求都会对应一个唯一不重复的Sessionid,然后呢就是用拦截器技术了,对用户的操作进行拦截,在拦截器中首先是对登录页面的相关url请求进行放行,然后还有的就是登录校验的请求放行。最后再对用户进行过滤,(关于用户登录,在验证成功时,不仅要把用户存入Session中用于登录拦截的验证,还需要把Session的ID存入用户表中对应的Sessionid)对于此次请求的request可以获取到Session及其ID然后再根据这个ID与数据库中的Sessionid进行比较是否相同相同则通过放行,不同则提示下线跳转登录,因为在一次用户连接服务器操作中会存在一个Session只要Session不过期每次用户发起的操作的request的Session的id都是一致的所以会和登录时存入数据库中的id匹配,当有其他人通过其他设备再用同一个账号进行登录时会重新建立Session会刷新数据库中的Sessionid而自己的Sessionid还是不变的,但是数据库中的Session已经发生了改变,所以在拦截器中校验数据库中Sessionid是否一致时就会失败从而拦截用户操作达到单用户登录的功能,(关于Session的说明,当用户初次与服务器连接时会在服务器端创建一个Session,而之后用户发起的每一次操作request中都会携带这个Session的id而出现一些情况Session的失效,用户长时间没有与服务器之间进行操作,用户退出登录主动让Session失效,用户关闭浏览器,或者服务器重新启动)

下面是SpringMVC中拦截器的实现

public class SingleUserInterceptor implements HandlerInterceptor {
	@Autowired
	private userMapper mapper;
	public void afterCompletion(HttpServletRequest arg0,
	HttpServletResponse arg1, Object arg2, Exception arg3)
	throws Exception {
		// TODO Auto-generated method stub
	}
	public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1,
	Object arg2, ModelAndView arg3) throws Exception {
		// TODO Auto-generated method stub
	}
	public Boolean preHandle(HttpServletRequest arg0, HttpServletResponse arg1,
	Object arg2) throws Exception {
		String url = arg0.getRequestURI();
		//如果拦截到的是登录的页面的话放行 
		if(url.indexOf("login.jsp")>=0||url.indexOf("new/login")>=0||url.indexOf("checkuser")>=0){
			return true;
		}
		//如果用户名存在放心(即登录放行) 
		Integer user = (Integer) arg0.getSession().getAttribute("user");
		if(user!=null){
			String sessionid = mapper.getUserEntity(Integer.valueOf(user)).getSessionid();
			if(sessionid.equals(arg0.getSession().getId())){
				return true;
			} else{
				arg1.setStatus(arg1.SC_GATEWAY_TIMEOUT);
				arg1.setContentType("text/html; charset=utf-8");
				PrintWriter out = arg1.getWriter();
				out.println("<html>");
				out.println("<script>");
				out.println("alert('您的账号在别地登录,您被迫下线')");
				out.println("window.open ('" + arg0.getContextPath()
				+ "/new/login','_top');");
				out.println("</script>");
				out.println("</html>");
				// arg0.getRequestDispatcher("login.jsp").forward(arg0, arg1);
				return false;
			}
		}
		arg0.getRequestDispatcher("login.jsp").forward(arg0, arg1);
		return false;
	}
}

以上的代码即可实现当同一个用户多次登录时,第一个登录用户在进行操作时会被挤下线的功能,可以实现先提示账号异地登录,然后跳转登录页面的功能。此处需要说明这个简单的alert提示功能的要点如果这要想出现这个alert必须能够时能够让请求结束,然后response的打印功能输出那个alert的提示语句,所以注释掉的那句跳转语句不能有,有则不会出alert提示语句,因为你的操作没有正确的结束而是被转发或者重定向为其他操作了不论正确与否都是其他的action向页面的输出了,所以你的response打印语句就不会出现。所以正确的做法是:拦截请求不在放行(returnfalse),此时请求还是原来的请求,并不会跳转,而response回页面的打印信息则可以显示,至于跳转登录页面则可以用:out.println("window.open('"+arg0.getContextPath()+"/new/login','_top');");来实现即在response所输出的一段js中执行了一个action请求,指向登录页面。

通过上面的代码基本上已经算是实现了单用户登录的功能,但是会发现ajax请求的操作并没有能够在异地登录时指向登录页面也没有提示信息,先说明一下原因,因为你的ajax时在进行一个异步请求,而且也在处理器映射器中匹配到了请求的url所以它会视为请求成功(返回状态码为200)而上面的response打印的js语句则会变为success中请求成功的,返回值,大家可以尝试一下把上面的这句话:arg1.setStatus(arg1.SC_GATEWAY_TIMEOUT);注释掉。这时需要进行一些其他操作,首先就是刚刚提到的语句arg1.setStatus(arg1.SC_GATEWAY_TIMEOUT);他的功能是设置response返回的状态码,上面的设置表示将返回状态码504代表请求出错,此时ajax请求就不会再在success方法中响应,而是会响应ajax的error方法,所以只需要再在ajax的error中执行对应的方法即可例如:

 $.ajax({
  url:'new/msd2',
  success:function(a){
  alert(a);
  },
  error:function(rs){
  if(rs.status==504){
  document.write(rs.responseText);
  }
  }
  }); 

当ajax请求因为异地登录被拦截时通过设置arg1.setStatus(arg1.SC_GATEWAY_TIMEOUT);可以使请求的返回状态变为504出错,然后由error方法响应,当判断到状态是自己所设置的状态码时再document.write(rs.responseText);responseText即为拦截器中所向前台打印的js语句,而要这些语句能够执行则需要document.write();方法将那些代码写入dom让其生效,至此,所有的请求,异步ajax请求的单点登录基本都已实现。

留此文只记录关键代码及思想及操作的原理

总结

以上就是本文关于SpringMVC拦截器实现单点登录的全部内容,希望对大家有所帮助。如有不足之处,欢迎留言指出。感谢朋友们对本站的支持。

相关文章

  • Linux中Java开发常用软件安装方法总结

    Linux中Java开发常用软件安装方法总结

    这篇文章主要介绍了Linux中Java开发常用软件安装方法总结,需要的朋友可以参考下
    2020-02-02
  • SpringBoot Application核心注解详解

    SpringBoot Application核心注解详解

    进入到@SpringBootApplication的源码,可以看到里面组合了三个我们感兴趣的注解@ComponentScan、@EnableAutoConfiguration、@SpringBootConfiguration,我们一一分析这三个注解
    2022-10-10
  • Spring MVC学习教程之RequestMappingHandlerAdapter详解

    Spring MVC学习教程之RequestMappingHandlerAdapter详解

    这篇文章主要给大家介绍了关于Spring MVC学习教程之RequestMappingHandlerAdapter的相关资料,文中通过示例代码介绍的非常详细,需要的朋友可以参考借鉴,下面随着小编来一起学习学习吧
    2018-11-11
  • Java 多线程等待优雅的实现方式之Phaser同步屏障

    Java 多线程等待优雅的实现方式之Phaser同步屏障

    在JAVA 1.7引入了一个新的并发API:Phaser,一个可重用的同步barrier。在此前,JAVA已经有CyclicBarrier、CountDownLatch这两种同步barrier,但是Phaser更加灵活,而且侧重于 重用
    2021-11-11
  • @Autowired注入为null的原因与解决方法

    @Autowired注入为null的原因与解决方法

    我们经常会通过@Autowired注解将某个类注到另一个类中,但是会发现注不进去,报NULL,所以本文就给大家分析了@Autowired 注入为null 的原因与解决方法,需要的朋友可以参考下
    2023-09-09
  • Spring和SpringBoot有哪些区别

    Spring和SpringBoot有哪些区别

    相信对于用了 SpringBoot很久的同学来说,还不是很理解 SpringBoot到底和 Spring有什么区别,看完文章中的比较,或许你有了不同的答案和看法。
    2020-10-10
  • java评论、回复功能设计与实现方法

    java评论、回复功能设计与实现方法

    很多项目或者系统都有评论或者回复的需求,但评论回复的实现往往都比较复杂,也不好实现,下面这篇文章主要给大家介绍了关于java评论、回复功能设计与实现的相关资料,需要的朋友可以参考下
    2022-06-06
  • ManyToMany单向、双向:@JoinTable的使用

    ManyToMany单向、双向:@JoinTable的使用

    这篇文章主要介绍了ManyToMany单向、双向:@JoinTable的使用方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-12-12
  • SpringSecurity概念及整合ssm框架的示例详解

    SpringSecurity概念及整合ssm框架的示例详解

    用户登录系统时我们协助 SpringSecurity 把用户对应的角色、权限组装好,同时把各个资源所要求的权限信息设定好,剩下的“登录验证”、“权限验证”等等工作都交给SpringSecurity,对SpringSecurity整合ssm框架相关知识感兴趣的朋友跟随小编一起看看吧
    2022-12-12
  • RabbitMQ 延迟队列实现订单支付结果异步阶梯性通知(实例代码)

    RabbitMQ 延迟队列实现订单支付结果异步阶梯性通知(实例代码)

    这篇文章主要介绍了RabbitMQ 延迟队列实现订单支付结果异步阶梯性通知,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-02-02

最新评论