https://mp.weixin.qq.com/s/TfJzvkF7B1lq_LNsbqMI0g
1.会话技术简介
http协议是无状态的,因此对于服务端来说,当它接收到客户端的http请求时,无法识别这个请求来源于哪个客户端。无状态的协议有优点也有缺点,但对于需要识别客户端甚至是需要记住客户端的业务来说,应当要让http协议"有状态"。
需要记住客户端的业务种类非常多。例如登陆系统,在一个页面登录后,新打开一个该网站页面,应当也保持登录状态。再例如购物车系统,某用户添加商品1后应当保证他还能继续添加商品2,在结算时能够读取购物车中的所有商品。
如何让服务端记住客户端?目前使用最多的是cookie和session两种会话技术。
- 1.Cookie:数据存储在客户端本地,减少服务器端的存储的压力,安全性不好,客户端可以清除cookie。
- 2.Session:将数据存储到服务器端,安全性相对好,会增加服务器的压力。
2.Cookie技术
Cookie技术是将用户的数据存储到客户端的技术,它的作用是为了让服务端根据每个客户端持有的cookie来区分不同客户端。
cookie由cookie name、具有唯一性的cookie value以及一些属性(path、expires、domain等)构成,其中value是区分客户端的唯一依据。
Cookie的原理为:服务端在接收到客户端首次发送的请求后,服务端在响应首部中加入"set-cookie"字段发送给客户端;客户端接收响应后,将cookie信息存储到内存中(如果设置了MaxAge属性,则存储到磁盘中);因为cookie数据在浏览器的内存中,因此无论是哪个页面,客户端再次向服务端发送请求时都能获取该cookie信息,并在请求首部中加入"cookie"字段发送给服务端;服务端借此就可以识别客户端,并从cookie中找到该客户端的信息。
使用Cookie需要解决的两个问题:
- (1).服务端怎样将一个Cookie发送到客户端。
- (2).服务端怎样接受客户端携带的Cookie。
2.1 服务器端向客户端发送Cookie
设置Cookie涉及的几个常用方法为:
Cookie(String cookie_name,String cookie_value)
:构造一个Cookie对象。setPath(uri)
:当访问属于该uri下的路径(包括子路径)时,该cookie都生效,例如setPath("/Cookie")
,当本机使用http://localhost/Cookie/servlet1
和http://localhost/Cookie/servlet2
访问时,都拥有该Cookie。setMaxAge(int second)
:设置该属性时,cookie将持久化保存到客户端的磁盘中,保存时间为second秒。如果cookie不具有该属性,则cookie只会存放在内存中。setDomain(String domain)
:设置Cookie生效的域范围,例如cookie.setDomain(".foo.com");
,这将对foo.com域下的所有主机都生效(如www.foo.com),但不包括子域(www.abc.foo.com)。
设置好Cookie后,需要使用response的方法addCookie(Cookie cookie)
将cookie加入到响应首部中发送给客户端。
例如,以下是名为CooikeDemo工程的一个servlet,该servlet的uri路径为"/cookieservlet"。
import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class CookieServlet extends HttpServlet { private static final long serialVersionUID = 1L; protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { Cookie cookie = new Cookie("username","zhangsan"); //构造cookie对象 cookie.setPath("/CookieDemo"); //设置cookie生效的uri范围 cookie.setMaxAge(10*60); //设置cookie持久到磁盘的时间为10分钟 response.addCookie(cookie); //在响应首部中加入set-cookie字段并发送给客户端 } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); }
该cookie将会在响应首部加入set-cookie字段发送给客户端:
当客户端再次请求时,将在请求首部中加入cookie字段。
需要注意的几点:
- (1).Cookie中不能存储中文。
- (2).如果不设置持久化时间,cookie会存储在浏览器的内存中,浏览器关闭时cookie信息销毁,这是会话级的cookie。如果设置持久化时间,cookie信息会被持久化到磁盘中,这是持久级别的cookie。持久化后的cookie不会随浏览器关闭而失效,而是在有效时间内都有效。
- (3).
setPath()
设置的生效路径为目录时,则cookie对该目录和子目录下的资源都生效,如果生效路径为文件时,则只对该文件有效。例如:cookie.setPath("/webapp"); //代表访问webapp应用中的任何资源都携带cookie cookie.setPath("/webapp/cookieservlet"); //代表访问webapp中的cookieservlet时才携带cookie信息
- (4).如果想要删除当前还有效的cookie信息,可以使用同名同路径的持久化时间为0的cookie进行覆盖。这样一来,每次客户端接收到响应后cookie就立即失效,也就无法携带cookie请求服务端。例如删除上面示例的cookie信息
Cookie cookie = new Cookie("username","zhangsan"); cookie.setPath("/CookieDemo"); cookie.setMaxAge(0); response.addCookie(cookie);
2.2 服务器端接受客户端携带的Cookie
如前面的图中所示,客户端的cookie信息是以请求头的方式发送到服务器端的。因此服务端要获取cookie信息,需要使用request对象中的方法getCookies()
。这时唯一的获取cookie的方法,它返回的是Cookie数组集合,因此需要遍历该数组才能获取指定名称的cookie。
例如,获取cookie name为"username"的cookie。
Cookie[] cookies = request.getCookies(); if(cookies != null) { for (Cookie coo : cookies) { String cookie_name = coo.getName(); if (cookie_name.equals("username")) { String cookie_value = coo.getValue(); System.out.println(cookie_name+":"+cookie_value); } } }
3.Session技术
从打开一个浏览器访问某个站点,到关闭这个浏览器的整个过程(释放浏览器内存),成为一次会话。除了Cookie技术可以让服务端在一次会话过程中记住客户端,Session技术也可以达到这样的目的。
Session技术将数据存储在服务器端,它会为每个客户端都创建一块内存空间存储客户端数据,并为客户端分配一个存储在cookie中的JSESSIONID,客户端需要每次都携带一个这个ID,服务器通过这个ID可以找到属于该客户端的内存空间。由于这个标识ID是借助Cookie存储的唯一性标识JSESSIONID,因此Session是基于Cookie来实现的。
Session的原理:服务端接收到某客户端首次发送的请求后,为此客户端生成一个session,并分配一段属于该session的缓冲区,同时将该session配对的标识号JSESSIONID作为cookie的name添加到响应首部中返回给客户端;客户端下次访问时,请求首部中将携带该JSESSIONID,服务端将根据该JSESSIONID寻找与之配对的session,如果能找到对应的session,则直接操作该session资源,否则将重新为此JSESSIONID分配一个session和对应的缓冲区。
使用Session技术需要解决如下三个问题:
- (1).怎样获得属于某客户端的session对象(内存区域)?
- (2).怎样向session中存取数据?
- (3).session对象的生命周期?
3.1 获得Session对象
服务端通过客户端发送cookie中的JSESSIONID区分客户端,可以通过请求包中的这个信息来获取该客户端相关的session信息。
HttpSession session = request.getSession();
此方法有两个作用:
- (1).从cookie中获取JSESSIONID,并寻找是否存在该ID对应的session对象。如果存在,则获取该session对象。
- (2).如果该客户端没有发送JSESSIONID或JSESSIONID和服务端记录的ID值不匹配,则为该JSESSIONID重新分配一个session对象。
实际上就是根据JSESSIONID判断该客户端是否在服务器上已经存在session了,有则用之,无则分配之。
3.2 向session中存取数据(session也是一个域对象)
session也是一个域对象,session域的作用范围是整个session,可以对客户端的多次请求生效。该范围小于context域(即application域),大于request域(只在一次请求内有效)。
作为域对象,session对象也同样具有如下三个方法:
session.setAttribute(String name,Object obj);
session.getAttribute(String name);
session.removeAttribute(String name);
此外,可以通过session对象的getId()
方法获取到该session的JSESSIONID值。
3.3 Session对象的生命周期
- 创建:第一次执行request.getSession()时创建。当客户端访问带有getSession()方法的servlet时会执行该方法并创建session,访问jsp页面也会,因为jsp默认设置session=true会创建session。但访问静态资源(html/pic)等不会,因为输出这些静态数据的默认servlet不执行getSession()。
- 销毁:
- 1.服务器(非正常)关闭时。
- 2.session过期/失效(默认30分钟,这个默认时间可以在web.xml中修改)。
<session-config> <session-timeout>30</session-timeout> </session-config>
- 3.手动销毁session:
session.invalidate();
。
也就是说,客户端在一次会话中任何资源都共用一个session对象。
问题:浏览器关闭,session就销毁了吗?
不对,session存储在服务端,和客户端没多大关系,只要客户端没有操作session,等一段时间后,session自动销毁。但是,关闭浏览器后,cookie中的JSESSIONID就丢失了,也就无法再找到对应的session数据。可以在发送session给客户端前将jsessionid当成cookie的属性并配置cookie的持久化时间持久化到客户端磁盘,这样再次打开浏览器时jsessionid就不会丢失。代码大致如下:
HttpSession session = request.getSession(); session.setAttribute("username","Tom"); String id = session.getId(); //获取JSESSIONID值 Cookie cookie = new Cookie("JSESSIONID",id); //"JSESSIONID"为固定值 cookie.setPath("/CookieDemo"); cookie.setMaxAge(12*60*60); //JSESSIONID持久化保存12小时 response.addCookie(cookie); response.getWriter().write("JSESSIONID:"+id); System.out.println(session.getAttribute("username"));