谈谈我熟悉又陌生的cookie

前言

大概是个人业务领域比较狭窄的缘由,我老是会据说cookie,却不多在实际的开发中应用或者实践过它,今天恰好看到<<JavaScript高级程序设计第三版>>的数据存储部分,说到了cookie,这里就对cookie作一个深刻访谈,但愿和我同样对cookie似曾相识的朋友能够真正的熟悉cookie,并学会利用cookie来服务咱们的业务.^_^^_^前端

Cookie

定义

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

限制

域名限制 数组

由于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的构成

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

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

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

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的比较好的使用策略欢迎在评论区留言或者私信我奥~共同进步是最让人开心的事儿呢~~~

相关文章
相关标签/搜索