真的了解Cookie吗 - Cookie详解

前言

看了很多博客文章,对一些概念都一直模模糊糊
希望能够对JavaWeb的一些知识深刻了解一下


目录

  1. HTTP的无状态
  2. Cookie
    2.1. Cookie API
    2.2. Cookie的属性
    2.3. 一个Cookie的使用
  3. 验证Path属性
  4. 购物车应用
  5. 总结

HTTP的无状态

一般说到HTTP协议的特性就会想到无状态,无连接

无连接就是每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接,这样太耗时且麻烦(毕竟Http协议是基于TCP协议的,而TCP需要三次握手连接,四次挥手断开连接)
随着版本发展的发展,HTTP1.1已经默认是长连接了,也就是说响应完后不会立即关闭连接,无连接这个特性可以告别了
(关于连接HTTP1.1还有并行连接及管道化等)

那无状态是什么呢?
以前学习HTTP协议的时候并没有深究,看到这篇文章后有一些懂了,特地来说说
http协议无状态中的 “状态” 到底指的是什么?!

百度百科中是这么解释的:

HTTP无状态协议,是指协议对于事务处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送的数据量增大。另一方面,在服务器不需要先前信息时它的应答就较快。


对同一个url请求没有上下文关系

笼统但不够深刻,不是特别的清晰

结合上面那一篇文章,认为HTTP协议的无状态是指:
HTTP协议对于客户端和服务器的会话过程,不会保存临时数据,服务器不会记忆客户端的信息,每一次请求都是独立的

这是种什么情况?
最好的例子就是仅靠HTTP协议的购物车问题(纯HTTP协议没有cookie、session等):

  1. 我们在客户端使用表单的get方法,通过URL地址后面带着我们的用户名、密码访问服务器,服务器验证了数据库的用户登录信息,响应“可以登录并跳转到购物界面”的信息给客户端(由于HTTP的无状态,服务器不保存这个登录信息)
  2. 我们在购物过程中,选中了一个商品,想要加入我们的购物车,这时客户端发送加入购物车的请求给服务器,问题来了: 由于服务器的无状态,服务器端不知道我们是谁(找不到用户,自然找不到购物车),返回“失败”信息给我们,如何解决呢?如果单靠HTTP协议,就只能在URL地址后面带上我们的用户名、密码

因为HTTP协议的无状态性,购物车有几个问题

  1. 操作繁琐,每次都需要带上我们的用户信息
  2. 不安全,在URL上带着用户名、密码,是个人都能盗号
  3. 频繁访问数据库,一次登录就需要一次数据库验证用户信息,每点一次加入购物车就需要直接往数据库里插入数据

所以说无状态中的状态是指:保存【不同的客户端访问的关系,不同访问需要保存的数据记录(URL中保存的用户信息)等】数据的缓存空间

这个缓存空间保存了用户名、密码,缓存了我们访问数据库的操作

就如同无连接问题被解决了,无状态问题也被大佬们解决了
一个是保存在客户端的Cookie,一个是保存在服务器端的Session
这两不是HTTP协议的内容,但是被各种Web容器、浏览器支持


Cookie

最先出现的是Cookie技术,后面的Session技术也是在Cookie上实现的

Cookie技术是为了解决HTTP协议的无状态的问题

还是前面的购物车问题:当我登录时,客户端发送请求,服务器记录下客户端的信息(给用户发一个通行证:Cookie),然后向客户端响应response中带上这个Cookie,浏览器保存这个Cookie,当用户想要将物品加入购物车,客户端发送加入购物车请求,就仅需带上Cookie通行证,有了这张通行证,服务器就记得这是哪个用户,然后就能简单的处理请求
在这里插入图片描述

知道了Cookie的思想,再来了解一下Cookie API

Cookie API

API 操作
public Cookie(String name,String value) 创建Cookie
setValue与getValue方法 设置、获得key-value键值对value属性
setMaxAge与getMaxAge方法 过期时间,在设置的某个时间点后该 Cookie 就会失效
setPath与getPath方法 设置、获得路径,Path是该 Cookie 是在当前的哪个路径下生成的
setDomain与getDomain方法 Domain是指生成该 Cookie 的域名,如 domain=“www.baidu.com”
getName方法 获得key-value键值对key值-name

Cookie是以键值对key-value保存数据
在这里插入图片描述

我们创建Cookie对象也是需要传入key-value键值对

Cookie的属性

我们查看源码,可以发现Cookie类有很多属性
在这里插入图片描述
我们来大致了解一下这些属性,这对理解Cookie有帮助

  • CookieNameValidator:判断Cookie的名字是否有效,需要满足一定的规则才可以作为Cookie的名字

源码文档是这么说的:

The name must conform to RFC 2109. That means it can contain only ASCII alphanumeric characters and cannot contain commas, semicolons, or white space or begin with a $ character. The cookie’s name cannot be changed after creation.

也就是必须符合RFC 2109

  • name 、 value

创建Cookie时传入
一个是Cookie的名字,一个是它的值,名字可以重复,值必须唯一
为什么是这样呢?
对于一个用户,名字可以重复的,但是value一定要唯一标识这个用户
也就是这个值是用来判断是哪个客户端(用户)

在这里插入图片描述

这里需要注意:private final String name;
name 是 final 修饰的,也就是说name是不可以更改的

  • serialVersionUID

序列号,是在序列化和反序列化中通过判断序列化来验证版本的一致性

  • version

版本,目前有两个版本,对应version值为0、1

version 0 : 由Netscape公司制定的,也被几乎所有的浏览器支持,Java中仅支持版本0。版本0限制了很多东西: Cookie的内容中不能出现空格,方括号,圆括号,等于号(=),逗号,双引号,斜杠,问号,@符号,冒号,分号

version 1:根据RFC2109文档制定的,放宽了很多限制。版本0中所限制的字符都可以使用。但为了保持兼容性,程序开发者要尽量避免使用这些特殊字符

  • comment

浏览器显示这个Cookie的时候显示出来的对这个Cookie的意义的说明信息

  • domain

指定关联的Web域名,如果希望多个Web服务器共享一个Cookie,可以设置domain
domain属性的默认值是创建cookie的网页所在服务器的主机名。不能将一个cookie的domain设置成服务器所在的域之外的域

例如设置为“.baidu.com”,所有以.baidu.com结尾的网站都可以使用该cookie

在这里插入图片描述

  • maxAge(Expires)

Cookie的生存期(失效时间),整数,单位为秒
当前maxAge = -1,maxAge为负数即浏览器关闭,Cookie失效,本地不保存
maxAge=0,表示从客户端电脑或浏览器内存中删除此cookie
maxAge为正数,该Cookie可以保存在客户端电脑,以Cookie文件保存,但是不论关闭浏览器还是电脑,都会在到期才会删除

  • path

Cookie的使用路径,也就是设置哪些路径可以访问这个Cookie
Cookie对应的页面(通常是一个目录),这个目录的子页面都可以访问这个Cookie,其他则不能访问,如果要设置为全局可以访问的,则设置为/

默认是/
在这里插入图片描述

  • secure

指定该Cookie是否被使用过安全协议传输
安全协议:在网络上传输数据之前先将数据加密

默认是false,也就是不安全的,仅通过http协议传输
可以设置为true,通过HTTPS、SSL等安全协议
在这里插入图片描述

  • httpOnly

限制cookie 对 HTTP 请求的作用范围

如果是true,只有在http请求头中会带有此cookie的信息,不能被其他方式如document.cookie来访问

为false就允许js等其他方式访问

默认是false

一个Cookie的使用

前面把Cookie的API和属性讲清楚了,可以实践一下

创建一个TestCookie类,继承HttpServlet类:

package test;

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

public class TestCookie extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setCharacterEncoding("GBK");
        Cookie cookie = new Cookie("name","zhangsan");
        cookie.setMaxAge(100);
        resp.addCookie(cookie);
        resp.getWriter().write("这是一个Test Cookie,名字:"+cookie.getValue());
        resp.getWriter().write("作用域为:"+cookie.getDomain());
    }
}

记得去web.xml配置访问路径

在这里插入图片描述

运行发现,Domain是null,而浏览器显示的是localhost?

那是因为我们在服务器端创建Cookie,还没有传Domain进Cookie对象,初始化是null


验证Path属性

Path属性是设置该Cookie能被哪些Servlet访问
默认是/

我们有了上面这个TestCookie类,再创建一个TestCookie1类

package test;

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

public class TestCookie1 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    	resp.setCharacterEncoding("GBK");
        Cookie[] cookies = req.getCookies();

        for(Cookie cookie : cookies){
            if (cookie.getValue().equals("zhangsan")){
                resp.getWriter().write("TestCookie1获得了Cookie,value="+cookie.getValue());
            }
        }

    }
}

记得要去web.xml配置一下访问路径

很简单,就是验证一下能不能得到vlaue='zhangsan’的Cookie

TestCookie创建Cookie成功

在这里插入图片描述

获得Cookie也成功了

在这里插入图片描述

所以说Path=’/’,就是所有的Servlet都可以获得Cookie
那我们设置一下Path
在TestCookie中添加。记得放在add方法前

cookie.setPath("/testCookie");

在访问http://localhost:8080/testCookie1

在这里插入图片描述

因为我是用数组迭代获得,因为没有Cookie给TestCookie1,所以空指针异常了

所以Path确实是限制Servlet访问Cookie

购物车应用

我们可以用Cookie来模拟购物车

写一个实体类Goods:

package test;

//货品类
public class Goods {
    private String goodName;
    private int goodId;
    private float goodPrice;

    public Goods(String goodName, int goodId, float goodPrice) {
        this.goodName = goodName;
        this.goodId = goodId;
        this.goodPrice = goodPrice;
    }

    public String getGoodName() {
        return goodName;
    }

    public int getGoodId() {
        return goodId;
    }

    @Override
    public String toString() {
        return  goodName + "仅卖" + goodPrice+" 元,快来买";
    }
}

先给商场装货物:ShopGoods

package test;

import java.util.LinkedList;

public class ShopGoods {
     LinkedList<Goods> goods= new LinkedList<Goods>();
    {
        goods.add(new Goods("苹果",1,10));
        goods.add(new Goods("辣条",2,5));
        goods.add(new Goods("毛巾",3,20));
    }

    public  LinkedList getAll(){
        return goods;
    }
}

上面两个都是普通的Java类
接下来完成Servlet

商场界面:

package test;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
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.io.PrintWriter;
import java.util.LinkedList;

public class ShoppingCar extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setCharacterEncoding("GBK");
        Cookie cookie = new Cookie("shop","zhangsan");

        ShopGoods shopGoods = new ShopGoods();
        LinkedList<Goods> goods = shopGoods.getAll();
        PrintWriter writer = resp.getWriter();
        writer.write("<html><body>");
        writer.write("---------------------商城大甩卖---------------------<br>");

        for (int i=0;i<goods.size();i++) {

            writer.write("---------------" +goods.get(i).getGoodName()+ "-----------------<br>");
            writer.write("<a href='/goodsInfo?id=" + goods.get(i).getGoodId() + "''target=_blank'> " + "查看信息并加入购物车</a><br>");
        }
        writer.write("</body></html>");

        writer.write("您的购物车:");
        Cookie[] cookies = req.getCookies();
        if (cookies != null){
            System.out.println(cookies.length);
            for (Cookie cookie1 : cookies){
                String name = cookie1.getName();
                for (Goods goods1:goods){
                    if (name.equals(String.valueOf(goods1.getGoodId()))){
                        writer.write(goods1.getGoodName()+" ");
                    }
                }
            }
        }

    }
}

货品界面:

package test;

import javax.servlet.ServletException;
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.io.PrintWriter;
import java.util.LinkedList;

public class GoodsInfo extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setCharacterEncoding("GBK");
        String id = req.getParameter("id");
        PrintWriter writer = resp.getWriter();
        ShopGoods shopGoods = new ShopGoods();
        LinkedList<Goods> all = shopGoods.getAll();
        writer.write("<html><body>");
        for (Goods good : all){
            if (good.getGoodId() == Integer.parseInt(id)){
                    writer.write(good.toString());
            }
        }
        Cookie cookie = new Cookie(id,id);
        resp.addCookie(cookie);

        writer.write("<a href='/shoppingCar' target=_blank'> " + "返回商店</a><br>");
        writer.write("</body></html>");
    }
}

记得在web.xml里添加这两个Servlet
运行:

在这里插入图片描述

购物车什么都没有,在点击一下苹果
在这里插入图片描述
返回商店
在这里插入图片描述

这里只是简单的完成了购物车的思想,没有前后端分离,而且仅仅依靠Cookie,显得有点粗糙


总结

  1. 详细的思考了HTTP协议的无状态性,无状态就是浏览器不会存储会话的临时数据
  2. 仔细的了解了Cookie的API及属性
  3. 简单是使用了一个Cookie,验证了Path属性
  4. 简单的实现了购物车

总结:算是理解了Cookie

发布了109 篇原创文章 · 获赞 31 · 访问量 7374

猜你喜欢

转载自blog.csdn.net/key_768/article/details/105096480