Java Web-Cookie和Session

会话

会话的概念

一次会话是指浏览器从浏览器第一次给服务器发送请求建立到其中一方断开结束会话的一个过程。它包含多次请求和响应。

会话是用来在一次会话的范围内的多次请求间共享数据的

会话分为两类:客户端会话技术(cookie)和服务器端会话技术(session)。

  • cookie:将数据存储到客户端
  • session:将数据储存到服务器端

Cookie

cookie保存在浏览器本地、一个会话中每一次请求都携带着它

快速入门

步骤:

  1. 服务器创建cookie对象,绑定数据
    new Cookie(String name,String value)
  2. 发送cookie对象给浏览器
    response.addCookie(Cookie cookie)
  3. 获取浏览器返回的cookie,拿到数据
    Cookies[] request.getCookies()

代码:

cookieDemo1.java

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        /*
        cookie快速入门
         */
        //1.创建cookie对象
        Cookie c=new Cookie("msg","hello");
        //2.通过添加到response从而发送cookie给客户端
    //这样客户端的浏览器上就会保存一个cookie了
        response.addCookie(c);
    }

cookieDemo2.java

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        /*
        cookie快速入门
         */
        //3.获取cookie,cookie可以有多个
        Cookie[] cookies = request.getCookies();
        //4.遍历cookies,获取数据
        if(cookies!=null){
            for (Cookie c:cookies
                 ) {
                String name = c.getName();
                String value = c.getValue();
                System.out.println(name+":"+value);
            }
        }
    }

注意,如果访问demo1和demo2分别使用的是两个不一样的浏览器,则实际上是两个会话,接收到的cookie是空的

原理分析

cookie的传递在响应头中,以键值对形式传递:set-cookie:msg-hello

浏览器收到后,将响应头中的数据保存到浏览器上(这也是为什么换浏览器就不能用了),下一次发送请求时将cookie放在请求头中发送到服务器:cookie:msg=hello

其实这些dirty work都被浏览器和服务器软件做了,程序员只是直接调用封装好了的API

有几个要点:

  1. 一次可以发送多个cookie:可以创建多个cookie对象,使用response调用多次addCookie方法发送cookie即可
  2. 默认情况下,cookie是保存在内存中的。当浏览器关闭后,cookie数据被销毁
    但是,cookie也可以设置以实现持久化存储,这是通过调用cookie的方法setMaxAge(int seconds),也即设置cookie的生命周期来实现:
  • 设置正数:将cookie数据写入硬盘文件中,持久化存储,传入的参数表示cookie的存活时间
  • 负数:默认值
  • 零:特殊情况,表示删除cookie信息(因为服务器不能操作客户端的文件,只能通过这种方式给出指令)
  1. 在tomcat8之前,cookie不能直接存储中文数据。在版本8以后就支持了,也不会有乱码问题。如果是8版本之前,需要先将中文数据转码,一般采用URL编码(也就是get方式传输中文参数时候使用的)
  2. cookie是有获取范围的:假设在一个tomcat服务器中部署了多个web项目,那么项目间的cookie默认不能共享。但也可以进行设置:setPath(String path),设置cookie的获取范围,path默认设置为当前虚拟目录(/xxx),但如果设置为/,也就是服务器的根路径,就可以在同一台服务器上的项目间共享cookie了
    但是,如何实现跨服务器的共享呢?这就需要cookie另一个方法setDomain(String path)了,如果设置一级域名相同,那么多个服务器之间cookie可以共享,例如:setDomain(".baidu.com"),那么只要一级域名是baidu.com的就可以共享
  3. cookie存储数据在客户端浏览器,相比于保存在服务器上,安全性会低很多,也很容易丢失
  4. 浏览器对于单个cookie的大小有限制(一般在4kb左右,因为cookie的本意就是用来存储小数据的),以及对于同一个域名下的总cookie数量有限制(一般限制在20个以内)。当然,我们在客户端也可以进行设置:

我们也可以对浏览器保存的cookie进行管理(这里显示的都是持久化存储的,也就是在硬盘中存储的):

cookie java 持久化 cookie javaweb_客户端

  1. 由于5和6,所以cookie一般用于存储少量的、不太敏感和重要的数据。cookie的作用是,在不登录的情况下,完成服务器对客户端的身份识别,以达到在不登录的前提下进行一些个性化设置并持久生效的效果。当然,如果登录了之后,就可以在服务器端记录用户的设置了,就不需要使用cookie这种方法了。

Session

概念

session是服务器端会话技术,在一次会话的多次请求间共享数据,将数据保存在服务器的对象中。

Session和cookie一样,都是域对象

快速入门

获取HttpSession对象:HttpSession session=request.getSession();

  1. Object getAttribute(String name)
  2. void setAttribute(String name,Object value)
  3. void removeAttribute(String name)

示例代码:

SessionDemo1.java

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //使用session共享数据
        //1.获取session
    //此时通过cookies中的session-id来获取该会话的session,如果没有的话就在服务器创建一个
        HttpSession session = request.getSession();
        //2.存储数据
        session.setAttribute("msg","hello session");
    }

SessionDemo2.java

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //使用session共享数据
        //1.获取session
        HttpSession session = request.getSession();
        //2.获取数据
        Object msg = session.getAttribute("msg");
        System.out.println(msg);
    }

同样的,换了个浏览器之后就是两次不同的会话了,不能共享数据

原理分析

  1. 服务器如何确保在一次会话范围内,多次获取的session对象是同一个?
    Session是依赖于cookie的!第一次获取session时,没有对应的cookie,此时会在内存中创建一个session对象并返回一个id,并通过set-session:JSESSIONID=id来讲session的id保存在浏览器的cookie中这样,在之后的请求中,服务器可以读取cookie中的session id,就知道该会话对应的session是哪个了。这也解释了为什么session是保存在服务器上的,但是依然只在一次会话中有效
  2. 客户端关闭前后,默认两次获得的session不是同一个(因为不是一个会话了,cookie换了),但是可以通过持久化保存cookie实现关闭浏览器后依然获得同样的session
  3. 从理论上来说,客户端不关闭、服务器关闭后,两次获取的session不一样。因为session也是保存在服务器内存而不是磁盘书上的
    但是这同样会造成问题:会造成用户数据的丢失、降低用户体验。如何解决这一个问题呢?
    解决方法就是采用session的序列化-反序列化
    首先,关于Java序列化,可以看我的这篇博文
  • 在服务器正常关闭之前,我们将session对象序列化后保存到硬盘上。session序列化之后的文件会保存在work目录下
  • 在服务器启动后,我们设置服务器自动地将硬盘中序列化后的session文件转为内存中的session对象

这是原理层面的,但是这部分内容已经被tomcat实现了:现在我们使用tomcat服务器,默认就实现了序列化-反序列化的功能。

但是,使用IDEA直接启动服务器、部署项目的话是不能实现该功能的。原因是IDEA在关闭-启动tomcat服务器时会执行一个操作:将work目录删除并重新创建,这就使得服务器关闭时保存在work目录下的session文件被销毁了。这也从某种意义上说明了IDEA只适合开发项目,真正部署项目到生产服务器上时还是直接启动tomcat好使。

  1. session什么时候会被销毁?
    session有三种情况会被销毁:
  1. 服务器关闭
  2. session对象调用invalidate()方法使自己失效
  3. session默认失效时间为30分钟。
    这个时间是可配置的。我们可以在tomcat的conf/web.xml中找到如下图的配置,修改这里的时间就能修改默认失效时间。
    同时,每一个项目也可以在自己的web.xml中单独进行设置,项目的设置会覆盖tomcat这里的默认设置

cookie java 持久化 cookie javaweb_服务器_02

  1. 和cookie只能存储字符串不同,session对存储的对象类型没有要求,对对象的大小同样没有要求。
    这一点也好理解:cookie要在每一次访问时传输,所以大小必须被严格控制。而session一直保存在服务器端不动,所以大小也就显得不重要了。也是因为同样的原因,session是数据安全的,而cookie则相对显得不安全
  2. 相比于我们在这篇博文介绍的ServletContext,我们更常使用session和cookie来存储一次会话的数据,因为它的作用域更合适、风险更小