本文主要阐述使用Filter实现用户自动登录和不使用Filter的用户登录案例之间的区别。之前在《Filter实现用户自动登录功能小结》一文简单介绍了Filter实现用户自动登录功能的实现逻辑,而本篇文章则是重点分析了其背后的设计思路。
先来看看案例各自需要的文件
案例1:Session实现用户登录
Login.html + User + IndexServlet +LoginServlet + LogoutServlet
案例2:Filter实现用户自动登录
login.jsp + index.jsp + User +LoginServlet + LogoutServlet + AutoLoginFilter
从上面来看的话,案例2多了一个AutoLoginFilter
过滤器。它的功能是对客户端的所有URL请求用户进行预处理。并且案例2少了一个IndexServlet
,多了一个index.jsp
(案例1中,登录后的结果通过IndexServlet在浏览器中打印出来
,不涉及jsp技术。而案例2中则是通过jsp技术
在浏览器中显示出来
)。
对案例逻辑进行简单的梳理
案例1的实现逻辑:
1、访问Login.html--->通过表单将参数(username和password)传递到LoginServlet。
2、LoginServlet获取表单传递过来的参数,如果参数验证通过
,那么就新建一个user对象并保存到Session对象的user属性中。
request.getSession().setAttribute("user",user);
如果参数验证不通过
,跳转到IndexServlet。
3、IndexServlet中,request请求调用getSession()方法获取session(根据cookies中的session的标识号跳转到对应的session。如果找不到标识号,那么就会自己创建一个session对象。LoginServlet和LogoutServlet都会跳转到IndexServlet这里,所以当访问IndexServlet时,不确定Session对象里面有没有user属性
存在),下面两行代码用于查找是否存在user对象
。
//创建或者获取保存用户信息的session对象
HttpSession session = request.getSession();
User user = (User)session.getAttribute("user");
如果user == null,说明不存在user对象,那么重新跳转到Login.html,如果user存在,那么就在浏览器中打印出已经登录的信息(该案例只要求打印出登录信息,不需通过jsp显示),并且当且仅当session的user属性指向一个user对象
时,使用Cookie存放session标识号,发送到浏览器中。
注意,当客户端首次访问该应用:由Login.html-->LoginServlet----跳转到--->IndexServlet
的时候,还没有发送cookies,但是这时候已经能通过request.getSession()方法获取session信息了(这就是上面说的,如果找不到JSESSIONID,那么就新建一个session)。
4、LogoutServlet将session对象的user属性进行清空,也即是说属性还在,但是user对象被删除了。
案例2的实现逻辑:
1、访问Login.jsp--->通过表单将参数(username和password)传递到LoginServlet
2、LoginServlet获取表单传递过来的参数,如果参数验证通过
,那么就新建一个user对象并保存到Session域中。
//将用户状态user对象存入session域
request.getSession().setAttribute("user",user);
按照上面的案例,应该接着跳转到IndexServlet,但是这里没有IndexServlet,相当于是把IndexServlet的代码整合到LoginServlet和LogoutServlet之中。
所以这里紧接着就可以发送Cookie了(而在案例1中跳转到IndexServlet时,不明确session中是否有user存在,所以发动Cookie前还要进行判断)。
//发动自动登录的cookie
String autoLogin = request.getParameter("autologin");
if (autoLogin != null){
Cookie cookie = new Cookie("autologin",username+"-"+password);
cookie.setMaxAge(Integer.parseInt(autologin));
cookie.setPath(request.getContextPath());
response.addCookie(cookie);
}
然后就可以跳转到index.jsp进行结果显示了。所以这里其实是把IndexServlet整合到LoginServlet好LogoutServlet中。
3、同样,LogoutServlet再删除掉session中的user信息后,也需要将cookies发送回客户端。
Cookie cookie = new Cookie("autologin","msg");
cookie.setMaxAge(0);
cookie.setPath(request.getContextPath());
response.addCookie(cookie);
接下来进行详细的分析
首先,使用Sesion技术实现用户登录说得通俗一点,就是能在Session对象中找到user对象。并且这个user对象的用户名和密码能通过验证。
(我们知道session可以储存信息,这里创建的user对象就是保存在session对象中,但具体而言user对象是保存到session对象的user属性
中的,所以我们要通过访问session对象的user属性来访问到user对象。)
用户首次登录必须通过表单
,服务器根据表单提交的用户细息来创建user对象,然后保存在session对象(简单理解就是服务器的一段内存空间)中。这在两个案例中都是相同的。
但是在案例1中,浏览器会把session的标识号装在cookie中发给浏览器,当再次发起URL请求时,request对象调用getSession()方法就会通过session标识号找到该session对象(相当于根据session标识号找到该内存空间),进而通过访问session对象的属性找到user对象。
Cookie Cookie= new Cookie("JSESSIONID",session.getID());
cookie.setMaxAge(60 * 30);
cookie.setPath("/chapter06");
response.addCookie(cookie);
也就是说,案例1的登录功能是通过查找已有的user对象
实现的。
再看看案例2中的发送给客户端的Cookie内容,现在是通过autologin变量来存储有用的username和password信息(如下所示)。
//发动自动登录的cookie
String autoLogin = request.getParameter("autologin");
if (autoLogin != null){
Cookie cookie = new Cookie("autologin",username+"-"+password);
cookie.setMaxAge(Integer.parseInt(autologin));
cookie.setPath(request.getContextPath());
response.addCookie(cookie);
}
我们发现不再发送session识别号到客户端了。这样做不就导致request调用getSession()方法时找不到位于特定内存空间的Session对象了吗。无所谓,现在我们根本不需要找到原来的session的对象。为什么呢,首先搞清楚我们之所以要去找session,是因为里面有我们想要的user对象,而我们想要user对象,无非还是想要获取user对象里面存储的登录信息(username和password)。
而现在我们已经将这个登录信息也放到Cookie中发送给客户端了(封装到autologin变量中),那我们就没有必要再去寻找原来的session了。我们可以从cookie中获取到用户信息,然后再重新据此创建user对象,保存在session对象的user属性中。所以,案例2和案例1不同的地方在于,案例2是重新创建并保存
user对象而不是从现有的session对象获取
user对象。这就跟表单登录很相似,唯一不同的地方在于表单登录时创建user对象是根据表单提供的信息,而Fillter对象创建对象是根据cookie提供的信息。
所以说,使用Filter实现自动登录,本质是在发起URL请求时通过Filter创建新的session。那么当发起多个URL请求时,就会创建多个session对象(保存到服务器的某个内存空间),当然,在每个URL请求中,都只有一个session变量。正常情况(如案例1)下,可以根据cookies中携带的sessionid来指向某个session对象。但是现在没有了sessionid,怎么知道对应到哪个session对象呢。答案就是不指定。
从AutoLoginFilter
的代码就可以看出来,在Filter中创建新的session对象,将其指向一个新的内存地址,然后接下来跳转到的Servlet使用session对象时,都是使用这个session对象。当然,如果访问新的路径(如/Newservlet),就会重新调用Filter新建一个session对象,然后Newservlet这个Servlet就会和Filter共享这个新的session,之所以能共享session,是因为只经过了过滤器Filter,并没有产生新的URL请求。
//创建或者获取保存用户信息的session对象
HttpSession session = request.getSession();
User user = (User)session.getAttribute("user");
而用户注销只需要将发送给客户端的cookies中的autologin的值做一些修改,这样就没有办法从中获取到有用的登录信息,更谈不上将登录信息封装成user对象放在session对象中了。
之所以还是需要user对象保存到session中,并不是因为URL请求中需要用到session内容,而是因为我们的jsp技术的需要
。jsp页面需要session中的内容:
...
...