Cookie

今日目标

  1. 1. 会话概述
  2. 2. cookie【重点】

一 会话概述

1.1 什么是会话?

日常生活中:从拨通电话到挂断电话之间的一连串你问我答的过程就是一个会话。

B/S架构中:从浏览器第一次给服务器发送请求时,建立会话;直到有一方断开,会话结束。

  1. 一次会话:包含多次请求响应。

1587172413825.png

1.2 会话技术

问题:Http是一个无状态协议,同一个会话的连续两个请求相互独立,彼此并不了解

作用:用于存储浏览器与服务器在请求和响应过程中产生的数据

  1. 在一次会话中(多次请求响应), 共享数据

客户端会话技术:cookie

服务器端会话技术:session

1587172824573.png

二 Cookie【重点】

2.1 概述

Cookie是客户端的会话技术

Cookie作用:在一次会话的多次请求之间共享数据,将数据保存到客户端(浏览器)

  1. # Cookie的特点
  2. 1. cookie保存在客户端(浏览器), 往往是由服务器产生发送给浏览器
  3. 2. cookie只能保存字符串, 格式是entry(name : value)
  4. 3. cookie的大小有限制: 4k
  5. 4. 一般, 同一域名下的cookie限制数量50

当当购物车

# 以游客的身份访问亚马逊
1. 第一次访问亚马逊, 添加一个商品到购物车
2. 关闭亚马逊
3. 接着再次访问亚马逊, 发现该商品依然在购物车

# 分析数据特点
    游客的数据价值不大    
# 第一个方案: 存在服务端
    1. Map存
        1). 占服务器内存
        2). 需要标记数据的归属, 不好标记
    2. 数据库存
        1). 占磁盘空间
        2). 需要标记数据的归属
    不推荐    
# 第二个方案: 存在浏览器端
    1). 不占用服务器存储空间
    2). 不需要标记数据的归属
    cookie
# 扩展: 如果是会员, 会话的购物车数据一般是保存在数据库    
    会员数据价值比较大
    会员的数据直接用会员id标记可以

2.2 快速入门

1630737668670.png

1. 设置数据到cookie中
    // 1.创建cookie对象,设置数据
        Cookie cookie = new Cookie(String name,String value);
    // 2.通过response,响应(返回)cookie
        response.addCookie(cookie);

2. 从cookie中获取数据
    // 1.通过request对象,接收cookie数组
        Cookie[] cookies = request.getCookies();
    // 2.遍历数组
        获取name值:     String name = cookie.getName();
        获取value值:     String value = cookie.getValue();

index.html

<a href="Cookie01Servlet">第一次访问亚马逊_添加商品到购物车</a> <br>
<a href="Cookie02Servlet">第二次访问亚马逊_查看购物车有数据</a> <br>
package com.itheima01.cookie;

import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;

@WebServlet("/Cookie01Servlet")
public class Cookie01Servlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        //1. 接收请求
        //2. 业务处理
            //用cookie记录购物车数据
        Cookie cookie = new Cookie("buycar", "huawei");
        //3. 响应
            //将cookie放在响应中发送给浏览器
        response.addCookie(cookie);
    }
}
package com.itheima01.cookie;

import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;

@WebServlet("/Cookie02Servlet")
public class Cookie02Servlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //从请求中获取所有的cookie
        Cookie[] cookies = request.getCookies();
        for (Cookie cookie : cookies) {
            String name = cookie.getName();
            String value = cookie.getValue();
            System.out.println(name + "-" + value);
        }
    }
}

google浏览器中查看Cookie

1591201737984.png

1599701855848.png

2.3 工作原理

基于HTTP协议:

1. 服务器发送Cookie给浏览器是通过 :  响应(响应头 set-cookie)

    Set-Cookie: phone=xiaomi

    Set-Cookie: computer=lenovo

2. 浏览器发送Cookie给服务器是通过:  请求(请求头  cookie)

    Cookie: phone=xiaomi; computer=lenovo

1587174808482.png

2.4 Cookie详情

2.4.1 服务器可以发送多个Cookie给浏览器吗?

* 答案: 可以的
    // 1. 创建多个cookie对象
        Cookie cookie1 = new Cookie("book","HighMath");
        Cookie cookie2 = new Cookie("music","lifeyun");
    // 2. 通过response响应多个
        response.addCookie(cookie1);
        response.addCookie(cookie2);
package com.itheima03.cookie;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet("/Cookie01Servlet")
public class Cookie01Servlet extends HttpServlet {

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //1. 存数据到cookie中
        Cookie cookie = new Cookie("book", "HighMath");
        Cookie cookie2 = new Cookie("music", "lifeyun");
        //2. 将cookie放到响应头,响应给浏览器
        response.addCookie(cookie);
        response.addCookie(cookie2);
    }

}

2.4.2 Cookie是否可以存储中文和非法字符?

* tomcat8之前的版本,不支持中文
* tomcat8以后的版本,支持中文...
    但是按照 Rfc6265Cookie规范,在cookie值中不能使用分号(;)、逗号(,)、等号(=)以及空格

我们可以存储,但是要使用url编码,来避开这个规范限制

* 解决
        java.net.URLEncoder.encode(字符串","utf-8") 把字符串使用utf-8进行编码
        java.net.URLDecoder.decode(字符串","utf-8")  把字符串使用utf-8进行解码
package com.itheima01.cookie;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;

/*
TODO:
* tomcat8之前的版本,不支持中文
* tomcat8以后的版本,支持中文...
    但是按照 Rfc6265Cookie规范,在cookie值中不能使用分号(;)、逗号(,)、等号(=)以及空格

我们可以存储,但是要使用url编码,来避开这个规范限制

* 解决
        java.net.URLEncoder.encode(字符串","utf-8") 把字符串使用utf-8进行编码
        java.net.URLDecoder.decode(字符串","utf-8")  把字符串使用utf-8进行解码
 */
@WebServlet("/Cookie03Servlet")
public class Cookie03Servlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        //1. 接收请求
        //2. 业务处理
            //用cookie记录购物车数据
        String str = "华为 为";
            //将utf-8编码的字符串进行url编码
            // 华为 为  ->  %E5%8D%8E%E4%B8%BA+%E4%B8%BA
            // url编码 : 空格 转换成  +
        str = URLEncoder.encode(str,"utf-8");
        Cookie cookie = new Cookie("phone", str);
        //3. 响应
            //将cookie放在响应中发送给浏览器
        response.addCookie(cookie);
    }
}
package com.itheima01.cookie;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLDecoder;

@WebServlet("/Cookie04Servlet")
public class Cookie04Servlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //从请求中获取所有的cookie
        Cookie[] cookies = request.getCookies();
        for (Cookie cookie : cookies) {
            String name = cookie.getName();
            String value = cookie.getValue();
            if("phone".equals(name)){
                //将value进行url解码(用utf-8)
                value = URLDecoder.decode(value,"utf-8");
            }

            System.out.println(name + "-" + value);
        }
    }
}

2.4.3 Cookie的域名作用(domain)

1. cookie信息中的域名作用是标记这个cookie的归属
        在我们的浏览器中,既保存了域名为localhost的cookie,又保存域名为baidu的cookie
        那么访问的网站如果是 http://localhost:8080, 浏览器的请求只会携带域名为localhost的cookie

2. 默认情况下,cookie的域名 和 发送此cookie的服务器域名是一致的
    url格式 ->  协议://域名:端口/资源位置

2.4.4 Cookie的路径作用(path)

# url格式
     协议://ip:port/资源位置?参数
     1). 资源位置 ->  /项目虚拟路径/项目中的资源路径
     2). 参数 -> name=value&name=value...
     http://localhost:8080/day17/Cookie04Servlet
0. 在我们的项目中,cookie的路径默认为项目的虚拟路径
            项目虚拟路径为 /day17    
1. 第一个作用: cookie信息中的path和name共同决定了cookie的唯一性
        a. Servlet被浏览器每访问一次, cookie就会发送一次
        b. 如果服务器再次发送一个同 path+name的cookie,会覆盖浏览器的那个cookie
            (新覆盖旧)
        c. 服务器再次发送一个同 path, 异name的cookie , 不会覆盖
        d. 服务器再次发送一个异path, 同name的cookie, 不会覆盖

2. 第二个作用: cookie的path还决定了cookie允许被访问的范围(有效路径)
    0). 浏览器访问的服务器资源路径跟cookie的路径相同,或者是cookie路径的子路径,才会携带这个cookie到服务器
    1). 例子
        I. 有两个cookie
            name=music,path=/day17
            name=book,path=/day17/abc
        II. 浏览器访问X网址,携带Y cookie
            当 X = http://localhost:8080/day17 
                Y = music
            当 X = http://localhost:8080/day17/abc/d
                Y = music,book
            当 X = http://localhost:8080/day17/aaa
                Y = music        
    2). 理解
        当 cookie.path = Z的时候, 只有访问Z以及Z的子路径才允许携带cookie(cookie才允许被服务器访问)
    3). 运用场景
        https://www.baidu.com:443/map : 地图
            cookie.path =  /map
        https://www.baidu.com:443/wenku : 文库
            /wenku
         访问文库,不会携带地图模块的cookie,实际也没必要       
#API : cookie.setPath(虚拟路径); 
    路径要以 / 开头
package com.itheima01.cookie;

import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;

@WebServlet("/Cookie01Servlet")
public class Cookie01Servlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        //1. 接收请求
        //2. 业务处理
            //用cookie记录购物车数据
        Cookie cookie = new Cookie("music", "oppo");
        Cookie cookie2 = new Cookie("book", "lenovo");
        //3. 响应
            //将cookie放在响应中发送给浏览器
//        cookie.setPath("默认当前项目虚拟路径");  // -> /day17
        cookie2.setPath("/day17/abc");
        response.addCookie(cookie);
        response.addCookie(cookie2);
    }
}

2.4.5 Cookie的存活时间

# 浏览器中cookie的信息
1. 创建时间: 浏览器接收到此cookie的时间
2. 到期时间: cookie销毁的时间

# cookie的存活时间有两种
1. 会话级别 (默认)
        浏览器关闭 : cookie会自动销毁

2. 持久级别(需要手动设置)
    cookie.setMaxAge(int second);
        正数:指定存活时间,单位是秒
        零:立即销毁
package com.itheima03.cookie;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet("/Cookie01Servlet")
public class Cookie01Servlet extends HttpServlet {

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //1. 存数据到cookie中
        Cookie cookie = new Cookie("book", "HighMath");
        Cookie cookie2 = new Cookie("music", "lifeyun");

        cookie.setMaxAge(60*30); // 30分钟 ,单位是秒
        //2. 将cookie放到响应头,响应给浏览器
        response.addCookie(cookie);
        response.addCookie(cookie2);
    }

}

2.4.6 Cookie的删除

# 目标:删除Cookie
    1. 用户在浏览器中手动删除cookie(清除浏览记录): 用户未必知道或者配合
    2. 从服务端远程操控删除cookie(服务端)

# 远程删除实现步骤:
    0. 核心思想: 服务端发送一个跟目标cookie同 path+name的cookie,此cookie的存活时间为0
          原理:  新cookie先覆盖浏览器保存的cookie,但是因为时间为0,新cookie马上死掉
package com.itheima01.cookie;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet("/Cookie05Servlet")
public class Cookie05Servlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

//        核心思想: 服务端发送一个跟目标cookie同 path+name的cookie,此cookie的存活时间为0
//        原理:  新cookie先覆盖浏览器保存的cookie,但是因为时间为0,新cookie马上死掉

        Cookie cookie = new Cookie("music", "");
        cookie.setPath("/day17"); //默认项目虚拟路径

        cookie.setMaxAge(0); // 立即销毁
        response.addCookie(cookie);
    }
}

2.5 Cookie特点

1. cookie存储数据在客户端(浏览器)
2. cookie的存储数据(name和value)只能是字符串
3. cookie单个大小不能超过4KB
4. 同一个域名下cookie数量一般不能超过50个
5. 同一域名下, cookie的path和name决定了它的唯一性
6. cookie存储的数据不太安全
        信息保存在用户的电脑上,都相对不安全