您的位置 首页 java

我的Java Web之路64 – 会话过期链接失效问题及转发和重定向解析

目录

  1. 介绍
  2. 问题描述
  3. 问题分析
  4. 会话过期后 session 对象的处理
  5. 转发
  6. 重定向
  7. 问题解决
  8. 总结

介绍

今天本来打算在 的基础上继续开发我们的租房网应用,结果在我先重新演示该应用的时候发现了一个如标题所示的问题。那么,本篇文章就把这个问题的解决过程记录下来。

重申一遍,记录作为学习、思考、实践、记录总结四部曲中的一步,无疑是很重要的。希望大家也可以经常记录一下自己的各种有意义的事情,特别是在当今自媒体特别流行且方便的时代,既能沉淀自己,还能帮助别人,甚至可以获取经济收益,何乐而不为呢!

问题描述

因为头条对文章的标题有字数限制,所以标题可能描述得不太清楚,在这里先复现一下问题。

首先,在Eclipse中发布租房网应用,并启动 Tomcat (可以参考 )。

然后,运行浏览器,输入我们租房网应用的登录页面URL:,打开登录页面:

我的Java Web之路64 - 会话过期链接失效问题及转发和重定向解析

没有注册的先注册,因为我们已经实现了用户注册功能(可以参考 )。已经注册的直接输入用户名和密码,点击登录按钮,打开房源列表页面:

我的Java Web之路64 - 会话过期链接失效问题及转发和重定向解析

然后等待30秒,因为我在应用的代码中设置会话的超时时间为30秒(参考 ):

 session.setMaxInactiveInterval(30);  

30秒过后,点击某条房源的链接(比如我这里点击的是第一条房源),此时因为会话已经过期失效,应用的代码中实现了登录验证,所以会自动跳转到登录页面。

我的Java Web之路64 - 会话过期链接失效问题及转发和重定向解析

然后,再次输入用户名和密码,登录之后打开和之前一样的房源列表页面,此时在会话过期之前再一次点击上面点击过的那条房源,此时按理来说应该会打开该房源的详情页面才对,结果却仍然打开登录页面!

问题分析

我们可以仔细观察一下点击房源之后打开的登录页面有什么不一样的地方, 实际上登录页面本身并没有什么不同的地方,但是此时浏览器 地址 栏的URL却是该房源详情页面的URL

后面每次(在会话过期之前)点击该房源打开的都是登录页面,就好像浏览器记住了该房源详情页面的URL对应的就是登录页面一样。

没错,这就是浏览器的缓存功能。 大致猜想是由于浏览器缓存造成的。

我们可以清楚浏览器的缓存数据再尝试(在会话过期之前)点击该房源一下,看看会不会打开该房源的详情页面。

因为我用的是谷歌浏览器,按照下图中箭头所指进行操作即可清除缓存,其他浏览器大家自行搜索如何清除缓存。

我的Java Web之路64 - 会话过期链接失效问题及转发和重定向解析

我的Java Web之路64 - 会话过期链接失效问题及转发和重定向解析

我的Java Web之路64 - 会话过期链接失效问题及转发和重定向解析

我的Java Web之路64 - 会话过期链接失效问题及转发和重定向解析

最后一步需要先勾选需要清除的数据,我这里是全部勾选。

此时,再重新登录租房网平台并(在会话过期之前)访问该房源,结果可以正确打开该房源的详情页面,而非登录页面。

但是,我们总不能要求用户每次在会话过期时都清除一次浏览器缓存吧,何况浏览器缓存还是很有用处的。

问题应该还是出在我们的后台代码上。 前面提到该问题一个很奇怪的现象就是打开的是登录页面而地址栏却仍是该房源详情页面的链接URL。

这说明在点击该房源链接时,浏览器会将该链接的URL填充到地址栏,同时以该URL为目标地址生成一个HTTP请求发送给HTTP服务器,这里是 Servlet 容器Tomcat,Tomcat会把请求交给Spring MVC的DispatcherServlet,DispatcherServlet又会寻找该URL对应的Handler,这里将寻找到以下Handler:

 	@GetMapping("/house-details.action")
	public ModelAndView getHouseDetails(HttpSession session, String houseId) {
		ModelAndView mv = new ModelAndView();
		if (session.getAttribute("loggedIn") == null) {
			mv.setViewName("login.html");
			return mv;
		}
		//查找房源详情并绑定到相应 jsp 页面,然后将请求转发到该页面
		mv.addObject("target", houseService.findHouseById(houseId));
		mv.setViewName("house-details.jsp");
		return mv;
	}  

该Handler先进行用户登录验证,因为会话已经过期,所以原会话中的数据已经全部被清除,或许此时处理这个请求时传进来的session是Tomcat重新生成的,等会我们可以寻找方法验证一下到底是数据被清除还是重新生成session。不管怎样,代码将会执行到

 mv.setViewName("login.html");   

即最后会返回登录页面,而此时浏览器地址栏的URL仍然是该房源详情页面的链接URL。所以,浏览器的缓存功能会将静态的登录页面缓存到本地,且与该URL绑定,后续用户每次点击该房源试图打开详情页面时都会先从缓存中寻找该URL对应的页面,即登录页面。

会话过期后session对象的处理

我们先增加一些日志代码来帮助我们分析问题,在所有Handler方法中将session对象和session的ID打印出来,即在方法的开头添加如下两行代码:

 		System.out.println("session: " + session);
		System.out.println("session id: " + session.getId());  

现在重新发布应用并启动Tomcat,在浏览器地址栏中输入登录页面的地址,打开登录页面,然后输入用户名和密码,登录租房网平台,此时在Eclipse的Console窗口中出现我们需要的日志:

我的Java Web之路64 - 会话过期链接失效问题及转发和重定向解析

可以看到此时把session对象和session的ID打印了两次,一次是在处理登录请求的Handler方法中,一次是登录成功后自动跳转到用户感兴趣的房源列表页面时处理获取房源列表请求的Handler中,此时的自动跳转代码是:

 mv.setViewName("redirect:houses.action");  

等待30秒,会话过期后,此时再点击第一条房源链接,此时再次打印出两条日志(在蓝色方框内):

我的Java Web之路64 - 会话过期链接失效问题及转发和重定向解析

这两条日志显然是执行获取房源详情的Handler方法时打印的。

从上图中也可以看到,这次的session对象是重新生成的,因此该对象与之前的不一样,当然,session的ID也与之前的不一样。

所以说,session过期后,Tomcat会销毁掉该session,当用户有新的请求到来时会生成新的session。

转发

30秒过后,点击某条房源的链接,此时因为会话已经过期失效,应用的代码中实现了登录验证,所以会自动跳转到登录页面,然而这种跳转仅仅是在应用的服务器端(即Tomcat服务器)的后台代码(实际上就是我们的Servlet和其他服务组件)中将登录页面作为此次点击该链接的响应,这种跳转就是 转发

实际上,我们也可以发现虽然页面是登录页面,然后浏览器地址栏中的URL仍然是房源列表页面中所点击房源的链接地址,这也间接说明 转发 实际上只是服务器端自己的行为,请求仍然只有浏览器点击某个链接而产生的那一个。

我的Java Web之路64 - 会话过期链接失效问题及转发和重定向解析

上图中的资源是泛指,可以是某个Servlet,可以是某个JSP,可以是静态资源,当然也可以是Spring MVC里面的某个Handler。当然,所有这些资源都是映射到一个URL上的。

在Spring MVC中实现转发的代码有多种,但基本原则是转发的目标地址是直接的,比如:

 mv.setViewName("login.html");  

而在Servlet规范中,转发的代码是要先从Servlet请求中获取指定目标地址的转发器,然后调用转发器的 forward ()方法,比如:

 request.getRequestDispatcher("login.html").forward(request, response);  

重定向

用户登录成功后自动跳转到用户感兴趣的房源列表页面时处理获取房源列表请求的Handler中,此时的自动跳转不仅会打开目标地址的页面,同时浏览器地址栏中的URL也会变为目标地址,这种跳转就是 重定向

重定向的本质是HTTP服务器处理完一个请求之后,在给浏览器的响应中告诉浏览器说,你去这个地址再发一次请求吧,于是浏览器就会以该地址填充到地址栏,同时以该地址生成一个新的请求发送到HTTP服务器(此时,这个HTTP服务器都有可能是与处理之前那个请求的HTTP服务器不同):

在Spring MVC中实现重定向的基本原则是目标地址需要添加一个前缀 “ redirect: ” ,比如:

 	@GetMapping("/house-details.action")
	public ModelAndView getHouseDetails(HttpSession session, String houseId) {
		ModelAndView mv = new ModelAndView();
		if (session.getAttribute("loggedIn") == null) {
			mv.setViewName("login.html");
			return mv;
		}
		//查找房源详情并绑定到相应JSP页面,然后将请求转发到该页面
		mv.addObject("target", houseService.findHouseById(houseId));
		mv.setViewName("house-details.jsp");
		return mv;
	}  

而在Servlet规范中,重定向的代码是直接用目标地址为参数调用Servlet响应的sendRedirect()方法,比如:

 mv.setViewName("login.html");   

问题解决

通过上面的分析,问题的根本原因还是在于我们进行登录验证失败的时候使用的是转发,导致浏览器将原请求URL与登录页面绑定缓存起来。

所以我们可以尝试将登录验证失败跳转到登录页面的代码改为重定向:

 		if (session.getAttribute("loggedIn") == null) {
			mv.setViewName("redirect:login.html");
			return mv;
		}  

现在我们再重新发布应用,启动Tomcat,并且还要清除一下浏览器的缓存,因为可能之前我们测试过的数据还在浏览器缓存中。

打开租房网平台登录页面,输入用户名密码登录之后会打开房源列表页面,等待30秒会话过期之后,再点击某条房源链接,此时会跳转到登录页面,但是浏览器地址栏中的URL也会变为登录页面的URL。

我们再次输入用户名密码登录之后会打开房源列表页面,马上点击某条房源链接,此时将正确打开该房源的详情页面,而不是打开登录页面,问题得到解决。

总结

  • 转发是服务端内部的行为,请求仍然是一个;
  • 重定向是服务端告诉浏览器重新访问另一个资源的行为,将会产生一个新的请求。
  • 在Servlet规范中,转发的代码是要先从Servlet请求中获取指定目标地址的转发器,然后调用转发器的forward()方法;
  • 在Servlet规范中,重定向的代码是直接用目标地址为参数调用Servlet响应的sendRedirect()方法;
  • 在Spring MVC中,转发的代码是直接返回资源URL;
  • 在Spring MVC中,重定向的代码是返回带前缀 “ redirect: ” 的资源URL;
  • session过期后,Servlet容器会销毁掉该session,当用户有新的请求到来时会生成新的session。

虽然我们解决了这个问题,但是我们还有别的问题需要考虑:

  • 同一个浏览器中可以同时登录两个不同的用户吗?
  • 会话过期用户重新登录后能够自动跳转回原来的页面吗?甚至是包含原来所填写表单数据的表单页面?
  • 等等。

文章来源:智云一二三科技

文章标题:我的Java Web之路64 – 会话过期链接失效问题及转发和重定向解析

文章地址:https://www.zhihuclub.com/186945.shtml

关于作者: 智云科技

热门文章

网站地图