10

前言

大概是我的业务领域比较狭窄的原因,我总是会听说cookie,却很少在实际的开发中应用或者实践过它,今天刚好看到<<JavaScript高级程序设计第三版>>的数据存储部分,说到了cookie,这里就对cookie做一个深入访谈,希望和我一样对cookie似曾相识的朋友可以真正的熟悉cookie,并学会利用cookie来服务我们的业务.^_^^_^

Cookie

定义

cookie,是服务器为了辨别用户身份,进行session跟踪而存储在用户本地终端上的数据(通常经过加密).

限制

域名限制

因为cookie一般用于与服务器进行交互,所以它一般存放在对应的域名下.当设定了一个cookie后,再给创建它的域名发送请求时,都会包含这个cookie,这个限制确保了储存在cookie中的信息只能让批准的接受者访问,而无法被其他域访问.

个数限制
由于cooki是存储在客户端计算机上的,还加入了一些限制确保cookie不会被恶意使用,同时不会占据太多磁盘空间.每个域的cookie总数是有限的,不过浏览器之间各有不同.

  • IE7和之后的版本每个域名最多50个.
  • Firefox限制每个域最多50个cookie
  • Opera限制每个域最多30个cookie
  • Safari 和Chrome 对每个域的cookie数量没有硬性规定.

当超过单个域名限制之后还要在设置cookie,浏览器会清除以前设置的cookie.IE和Opera会删除最近最少使用过的cookie.所以考虑cookie限制非常重要,避免出现不可预期的后果.

尺寸限制
浏览器中对于cookie的尺寸也有限制,大多数浏览器是4KB的长度限制,尺寸限制影响一个域下所有的cookie,而并非每个cookie单独限制.
如果你尝试创建查过最大限制的cookie,那么该cookie会被悄无声息地丢掉.

cookie的构成

cookie由浏览器保存的以下几块信息构成.

  • 名称(name): 一个唯一确定cookie的名称.
  • 值(value): 存储在cookie中的字符串值.
  • 域(domain): cookie对于哪个域是有效的,控制只有向该域发送的请求才会包含这个cookie.
  • 路径(path): 对于指定域中的哪个路径,应该向服务器发送cookie.
  • 失效时间(expires): 表示cookie何时会被删除的时间戳,没有设置则默认是浏览器会话结束时,即将所有cookie删除,若设置的失效日期是以前的时间,则cookie会被立刻删除.
  • 安全标志(secure): 制定后,cookie只有在使用SSL链接的时候才会发送到服务器,即https请求才可以发送cookie.

注意发送cookie的时候只会发送cookie的名和值才会被发送,其他值只会cookie信息的描述.

cookie的使用

使用场景

常用场景: cookie一般用来做登录验证,用户登陆的时候讲用户名和密码传入到服务器端,服务器会返回将用户相关的认证信息,然后由服务器将这些信息写入cookie或者由前端使用js操作cookie将这些信息写入到cookie中(如果服务器通过Set-Cookie的方式直接写入则不需要前端的参与,前端是无感知的),登陆成功以后的用户在该域名下的访问都会在请求中发送cookie,作为该用户的身份标识.我们这里主要讨论的是前端使用js操作cookie的情况.

不常用场景: 我们也可以用js操作cookie,在cookie上存储我们临时需要的用于页面交互的变量,这个时候cookie就充当了sessionStorage或者localStorage的角色.

操作cookie

由于JavaScript中读写cookie不是非常直观,常常需要写一些函数来简化cookie的功能.基本的操作有三种: 读取,写入,删除;

说明: 我不知道看这篇文章的朋友是不是了解这些操作cookie的方法,如果不了解,我建议你先想一想,然后尝试着自己去写,然后感兴趣的话再来看看我的代码,也可以分享到评论区,我们一起来看看这些实现方法的优劣,不知道不同思想的碰撞会不会擦出奇妙的火花呢? 很期待奥~

封装的操作cookie的代码如下:

const CookieUtil = {
    // 获取cookie 接受的参数 cookie的名称
    get: function(name) {
        var cookieName = encodeURIComponent(name) + "=",
            cookieStart = document.cookie.indexOf(cookieName),
            cookieValue = null;
        if(cookieStart !== -1) {
            var cookieEnd = document.cookie.indexOf(";",cookieStart);
            if(cookieEnd == -1) {
                cookieEnd = document.cookie.length;
            }
            cookieValue = decodeURIComponent(document.cookie.substring(cookieStart + cookieName.LENGTH, cookieEnd));
        }
        return cookieValue;
    },
    // 设置cookie, 接收参数: cookie的名称, cookie的值, 
    // 可选的用于执行cookie何时应被删除的Date对象,cookie的可选的URL路径, 可选的域和是否要添加secure标志的布尔值
    set: function (name, value, expires, path, domain, secure) {
        var cookieText = encodeURIComponent(name) + "=" + encodeURIComponent(value);
        if(expires instanceof Date) {
            cookieText += "; expires=" + expires.toGMTString();
        }
        if(path) {
            cookieText += "; path=" + path;
        }
        if(domain) {
            cookieText += "; domain=" + domain;
        }
        if(secure) {
            cookieText += "; secure";
        }
        document.cookie = cookieText;
    },
    // 删除cookie的方法, 接收的参数: 要删除的cookie的名称,可选的路径参数,可选的域参数和可选的安全参数
    unset: function (name, path, domain, secure) {
        // 将某个cookie的过期时间早于当前时间,则会被立刻删除,该方法设置失效时间为1970年1月1日
        this.set(name, "", new Date(0), path, domain, secure);
    }
}

// 设置cookie
CookieUtil.set("name", "Nicholas");
CookieUtil.set("book", "Professional JavaScript");

// 读取cookie的值
CookieUtil.get("name");
CookieUtil.get("book");

// 删除cookie
CookieUtil.unset("name");
CookieUtil.unset("book");

子cookie

为了绕开浏览器的单域名下的cookie数限制,一些开发人员使用了一种成为子cookie的改变,子cookie是存放在单个cookie中的更小段的数据,通常是多个名称值对的形式.子cookie对常见的格式如下所示:
namename1=value1&name2=value2&name3=value3&name4=value4&name5=value5
子cookie一般也以查询字符串的格式进行格式化,然后这些值可以使用单个cookie进行储存和访问,而非对每个名称-值对儿使用不同的cookie存储,最后网站或者web应用程序可以无需大到单域名cookie上限也可以存储更加结构化的数据.
为了更好的操作子cookie,必须建立一系列新方法,子cookie的解析和序列化会因子cookie的期望用途而略有不同并更加复杂些,例如,要获得一个子cookie,首先要遵循与获得cookie一样的基本步骤,但是在解码cookie值之前,需要操作字符串,遍历数组之类的操作来找出子cookie的信息.

说明: 我不知道看这篇文章的朋友是不是了解这些操作cookie的方法,如果不了解,我建议你先想一想,然后尝试着自己去写,然后感兴趣的话再来看看我的代码,也可以分享到评论区,我们一起来看看这些实现方法的优劣,不知道不同思想的碰撞会不会擦出奇妙的火花呢?

操作子cookie的方法如下:


// 操作子cookie的一组方法
var SubCookieUtil = {
    // 获取cookie, 接收两个参数,cookie名和子cookie名
    // 如果不穿子cookie名,则是普通的获取方法,如果传了,则取对应子cookie名的value.
    get: function (name, subName) {
        var subCookies = this.getAll(name);
        if(subCookies) {
            return subCookies[subName];
        } else {
            return null;
        }
    },
    // 判断如果cookie中name对应的value不包含子cookie,
    // 则返回解码后的cookieValue,如果包含子cookie,则返回处理后的result对象
    getAll: function (name) {
        var cookieName = encodeURIComponent(name) + "=",
            cookieStart = document.cookie.indexOf(cookieName),
            cookieEnd,
            result={},
            cookieValue= null,
            i,len,subCookies='',
            parts = [];
        if(cookieStart !== -1) {
            cookieEnd = document.cookie.indexOf(";", cookieStart);
            if(cookieEnd == -1) {
                cookieEnd = document.cookie.length;
            }
            cookieValue = document.cookie.substring(cookieStart + cookieName.length, cookieEnd);
            if(cookieValue.length > 0) {
                if(cookieValue.indexOf("&") > -1) {
                    subCookies = cookieValue.split("&");
                    console.log("get subCookies",subCookies);
                    for(i = 0,len = subCookies.length; i < len; i++) {
                        parts = subCookies[i].split("=");
                        result[decodeURIComponent(parts[0])] = decodeURIComponent(parts[1]);
                    }
                } else {
                    parts = cookieValue.split("=");
                    result[decodeURIComponent(parts[0])] = decodeURIComponent(parts[1])
                }
                return result;
            }
        }
        return null;
        
    },
    // 同样的,要设置子cookie,也有新的set方法
    set: function(name, subName, value, expires, path, domain, secure) {
        
        var subCookies = this.getAll(name) || {};
        subCookies[subName] = value;
        this.setAll(name, subCookies, expires, path, domain, secure);
    },
    setAll: function(name, subCookies, expires, path, domain, secure) {
        var cookieText = encodeURIComponent(name) + "=",
            subCookieParts = [],
            sub, result;
        // 将subCookies对象里的cookie值对编码,并放进subCookieParts数组中
        for(sub in subCookies) {
            if(subCookies.hasOwnProperty(sub)) {
                subCookieParts.push(encodeURIComponent(sub)+ "=" + encodeURIComponent(subCookies[sub]));
            }
        }
        if(subCookieParts.length > 0) {
            cookieText += subCookieParts.join("&");
            if(expires instanceof Date) {
                cookieText += "; expires=" + expires.toGMTString();
            }
        } else {
            cookieText += "; expires=" + (new Date(0)).toGMTString();
        }
        if(path) {
            cookieText += "; path=" + path;
        }
        if(domain) {
            cookieText += "; domain=" + domain;
        }
        if(secure) {
            cookieText += "; secure";
        }
        document.cookie = cookieText;   
    },
    // 删除cookie 删除单个cookie
    unset: function(name, subName, path, domain, secure) {
        var subCookies = this.getAll(name);
        subCookies[subName] ? delete subCookies[subName] : '';
        this.setAll(name, subCookies, null, path, domain, secure);
    },
    //删除cookie  删除多个cookie  
    unsetAll: function (name, path, domain, secure) {
        this.setAll(name, null, new Date(0), path, domain, secure);
    }  
}

// 假设 document.cookie = "data=name=Nicholas&book=Professional%20JavaScript"
// 设置两个cookie
SubCookieUtil.set("xiaosisi", "name", "Nicholas");
SubCookieUtil.set("xiaosisi", "book", "Professional JavaScript");
SubCookieUtil.set("xiaosisi", "sisisi", "撕撕撕");
// 设置全部子cookie和失效日期
SubCookieUtil.setAll("xiaosisi", {name: "Nicholas", book:"Professional JavaScript",  sisisi: "撕撕撕"}, new Date("2018-10-25"));
// 修改名字的值,并修改失效日期
SubCookieUtil.setAll("xiaosisi", "name", "MIrascl", new Date("2018-11-25"));

// 删除名为sisisi的子cookie
SubCookieUtil.unset("xiaosisi", "sisisi");
// 删除整个cookie
SubCookieUtil.unsetAll("xiaosisi");

总结

关于cookie有两点需要注意的地方:

第一: 由于所有的cookie都会由浏览器作为请求头发送,所以在cookie中存储大量信息会影响到特定域的请求性能,cookie信息越大,完成对服务器请求的时间也就越长.尽管浏览器对cookie的大做了限制,不过最好还是尽可能在cookie中少存储信息,以免影响性能.

第二:一定不要在cookie中存储重要和敏感的数据.cookie的存储不是很安全,其中包含的任何数据都可以被他人访问,重要的用户信息不建议存储在cookie里.

如果读者们有关于cookie的比较好的使用策略欢迎在评论区留言或者私信我奥~共同进步是最让人开心的事儿呢~~~


慢思考快行动
361 声望22 粉丝

有梦想但又不失风趣的程序员