JS使用Cookie

0. Cookie概念

定义

记录服务器与客户端之间的状态,最早用来判断用户是否第一次访问网站。

现在已经是识别用户,实现持久会话的较好方式。 它定义了一些新的HTTP首部。

作用

Cookies的作用就是用于解决“如何记录客户端的用户信息”
当用户访问web页面时,它的名字可以记录在cookie中,当下一次访问时可以
在cookie中读取用户的访问记录。

Cookie的类型
  • 会话cookie
    会话cookie是一种临时cookie,记录了用户访问站点的设置和偏好preference。 用户退出浏览器时,会话cookie就被删除了。
  • 持久cookie
    持久cookie是存储在硬盘上的,浏览器退出后,计算机重启时它们仍然存在。持久cookie维护某个用户周期性访问的信息,比如登录名。
Cookie的本质

Cookie本质就是一个名值对构成的字符串。
一般是以下这些信息构成:
名称 :需要被URL编码,不区分大小写
:必须被URL编码
: 对哪个域是有效的。默认就是发送cookie的那个域
路径: 对于指定域中的那个路径。 比如指定了
http://www.wrox.com/books , 那么
http://www.wrox.com的页面就不会发送cookie!~

失效时间(过期时间): 何时被删除的时间戳。默认是浏览器关闭时候所有cookie删除。 这个值是一个GMT格式的时间 (Wdy,DD-Mon-YYYY HH:MM:SS GMT)
安全标志 指定后,cookie只有在使用SSL连接的时候才发送到服务器。

HTTP response示例

HTTP/1.1 200 OK
Content-type:text/html
Set-Cookie:name=value; expire=Mon,22-Jan-07 07:10:24 GMT; domain=.wrox.com; path=/;secure

值得注意的是:域 路径 失效时间 secure标志都是服务器给浏览器的指示,指定何时发送cookie。这些参数不会作为发送给服务器的cookie信息的一部分,只有名值对会发送!
Secure是唯一一个非名值对的。

客户端存储的cookie示例
如:

Cookie: NOWCODERUID=7CAEE918A9211F1F5B3D8DB4913496AB; t=1204D82E035A9BD09D0AA90A96DBA998; NOWCODERCLINETID=CBD2F59F5E8C7B7403269B5E9E1BEAC7; CNZZDATA1253353781=155670659-1487244501-https%253A%252F%252Fwww.nowcoder.com%252F%7C1487472599; SERVERID=aff739a092fc0d444b24c3a30d4864b6|1487477718|1487244493

持久Cookie和会话cookie的区别其实就是过期时间不同。 一般是Expire或Max_Age来决定。

Cookie的局限
  1. IE6或更低版本最多20个cookie
  2. IE7和之后的版本最后可以有50个cookie。
  3. Firefox最多50个cookie
  4. chrome和Safari没有做硬性限制,
    IE和Opera 会清理近期最少使用的cookie,Firefox会随机清理cookie。
  5. cookie的最大大约为4096字节,为了兼容性,一般不能超过4095字节。

1.COOKIE处理:

服务器向客户端发Cookie
COOKIE处理:
A. 服务器向客户端发Cookie
例如:

HTTP/1.1 200 OK
Content-type:text/html
Set-Cookie:name=value
Other-header:other-header-value

B 浏览器将Cookie保存
C 每次浏览器会将Cookie发到服务器端
例如:

GET /index.html HTTP/1.1
Cookie:name=value
Other-header:other-header-value

如下图:
这里写图片描述
我们的Web服务器会通过HTTP扩展首部 Set-Cookie或Set-Cookie2 把cookie贴到每个用户上。
浏览器会存储从服务器返回的Set-Cookie中的内容,并将cookie存储在浏览器的cookie文件夹中。也就是说会存储在硬盘上。

IE对Cookie的处理

IE将cookie存储在高速缓存目录下独立的文本文件中。可以在硬盘中查看到。
这里写图片描述

IE特有的cookie格式文件

CNZZDATA3258975
cnzz_eid%3D958325568-1466428170-http%253A%252F%252Fwww.cnki.net%252F%26ntime%3D1473251452
adp.cnki.net/
1600
3240902912
30578703
2587134064
30542091
*

Cookie一个接一个地存储在文件中,每个cookie由多行构成。
名字 CNZZDATA3258975
值 cnzz_eid%3D958325568-1466428170-http%253A%252F%252Fwww.cnki.net%252F%26ntime%3D1473251452
域/路径 adp.cnki.net/

不同web站点使用不同的cookie

浏览器中可能有成百上千的cookie,但浏览器不会把每个cookie都发给所有的站点! 也就是说哪个站点产生的cookie,就会发送给哪个。
这就是所谓的绑定域名,设定了一个cookie后,再给创建它的域名发送请求时都会包含这个cookie~

原因:

  • 对所有cookie进行传输会严重降低性能。浏览器实际传输的cookie字节数比实际内容字节数多!
  • cookie中包含的是服务器特有的名值对,大部分站点来说,大多数cookie没有意义
  • 将所有的cookie发送给所有站点会引发潜在的隐私问题,那些你不信任的站点也会获得其他站点的信息。

例如: joes-hardware.com 产生的cookie只会发送给joes-hardware.com,不会发送给baidu.com

2.客户端JS操作Cookie

document.cookie=”username=John doe”;
创建cookie
可以添加一个过期时间,默认是浏览器关闭时删除
您可以使用 path 参数告诉浏览器 cookie 的路径。默认情况下,cookie 属于当前页面。
例如:

document.cookie="username=John doe;expire=Thu, 18 Dec 2013" 

读取Cookie

var x=document.cookie;
var ca=x.split(";");

修改cookie :
document.cookie=”xx” 就会自动覆盖旧的cookie了

var x=document.cookie;
x="ID=223;exire="THU,18,DEC 2015";
//常用用法
document.cookie=cname+"="+cvalue+": "+expires;

查询cookie

function checkCookie(){
    var user=getCookie("username");
    if(user!=""){
        alert("Welcome again"+user);
    }
    else{
        user=prompt("Please enter your name:","");
        if(user!=""&&user!=null){
            setCookie("username",user,30);
        }
    }
}

封装操作
写一个CookieUtil对象来包含这些处理
注意,其实BOM提供了专门的函数decodeURIComponent和encodeURICompoent来URL编码和URL解码!

document.cookie解释
当用来获取属性值时,返回当前页面可用的(根据cookie的域、路径、失效时间和安全设置) 所有cookie字符串。 由分号分隔。

当用来设置时,设置一个新的cookie字符串,不会覆盖,只会被解释并添加到现有的cookie集合中。
除非设置的cookie名称已经存在。

代码:

var CookieUtil={
    get:function(name){
        var cookieName=encodeURIComponent(name)+"=",
            cookieStart=document.cookie.indexOf(";"+cookieName),
            cookieValue=null;
        if(cookieStart>-1){
            var cookieEnd=document.cookie.indexOf(";",cookieStart); //;cookieStart 的index
            if(cookieEnd==-1){
                cookieEnd=document.cookie.length;
            }
            cookieValue=decodeURIComponent(document.cookie.substring(cookieStrart+cookieName.length,cookieEnd ));
        }

        return cookieValue;
    },

    set:function(name,value,expires,path,domain,secure){
        var cookieText=encodeURIComponent(name)+"="+encodeURIComponent(value);
        if(expires instanceof Date){//instanceof
            cookieText+="; expires="+expires.toGMTString();
        }
        if(path){
            cookieText+="; path="+path;
        }
        if(domain){
            cookieText+="; domain="+domain;
        }
        if(secure){
            cookieText+="; secure"
        }
        document.cookie=cookieText;
    },
    unset:function(name,path,domain,secure){
        this.set(name,"",new Date(0),path,domain,secure);
    }
}

解释
get()方法根据cookie的名字获取相应的值。它会在document.cookie字符串中查找cookie名加上”=” 的位置。 如果找到了,使用indexOf查找该位置之后第一个分号。 如果没有找到分号,就说明这个cookie是字符串最后一个, 则余下的字符串都是cookie的值。 用decodeURIComponent解码并返回。

set()方法很简单,接收前面介绍的几个参数值。除了值以外的都是可选的。 名值进行了URL编码。其他就是添加 分号 和本身的关键字。

删除没有直接方法,我们只能使用unset,它的原理是设置 相同的路径,域,和安全选项再次设置cookie,并将过期时间设置为过去的时间!
new Date(0) 就是 1970年1月1日返回的值。

子cookie

为了突破浏览器单域名下的cookie数量的限制,使用子cookie。 它就是存放下单个cookie下的跟小段的数据。 就是使用cookie值来存放多个名值对!
如:
name=name1=value1&name2=value2&name3=value3
子cookie以查询字符串的格式进行格式化。 这些值可以使用单个cookie进行存储和访问! 但是注意的是全部cookie的大小仍然限制在4KB。

var SubCookieUtil={
    get:function(name,subName){
        var subCookies=this.getAll(name);
        if(subCookies){
            return subCookies[subName];
        }else{
            return null;
        }
    },
    // name=value;  cookieStart就是 name=   cookieEnd就是 value;
    getAll:function(name){
        var cookieName=encodeURIComponent(name)+'=',
            cookieStart=document.cookie.indexOf(cookieName),
            cookieValue=null,
            cookieEnd,
            subCookies,
            i,
            parts,
            result={};

        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){
                subCookies=cookieValue.split("&");
                for(i=0,len=subCookies.length;i<len;i++){
                    parts=subCookies[i].split("=");
                    result[decodeURIComponent(parts[0])]=decodeURIComponent(parts[1]);
                }
                return result;
            }
        }
    }
    return null;
};

对于获取子cookie。 其中get()方法获取单个子cookie的值,getAll()获取所有子cookie。
get()方法其实就是调用getAll()获取所有的子cookie,返回所需的那个。不存在就返回null。

而getAll()方法和CookieUtil.get()方法相似关键是 先根据&解析每个子cookie并放到一个数组中,然后对于每个子cookie再根据=分割。 这样在parts数组中前一部分就是cookie名字后一部分就是cookie值。

var SubCookieUtil={
    get:function(name,subName){
        var subCookies=this.getAll(name);
        if(subCookies){
            return subCookies[subName];
        }else{
            return null;
        }
    },
    // name=value;  cookieStart就是 name=   cookieEnd就是 value;
    getAll:function(name){
        //URL编码,其实就是对一些特殊字符进行转码!例如把中文"你好"
        //转成 %e4%bd%a0%e5%a5%bd 百分号的形式
        var cookieName=encodeURIComponent(name)+'=',
            cookieStart=document.cookie.indexOf(cookieName),
            cookieValue=null,
            cookieEnd,
            subCookies,
            i,
            parts,
            result={};

        if(cookieStart>-1){
            //找到cookie的后面部分,从cookieStart开始搜索
            cookieEnd=document.cookie.indexOf(";",cookieStart);
            if(cookieEnd==-1){
                //是否为末尾,为-1就是末尾,没找到。
                cookieEnd=document.cookie.length;
            }
            cookieValue=document.cookie.substring(cookieStart+cookieName.length,cookieEnd);
            if(cookieValue.length>0){
                //先分割出每一个子cookie
                subCookies=cookieValue.split("&");
                for(i=0,len=subCookies.length;i<len;i++){
                    parts=subCookies[i].split("=");
                    result[decodeURIComponent(parts[0])]=decodeURIComponent(parts[1]);
                }
                return result;
            }
        }
        return null;
    },
//设置子cookie
    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)+"=",
            subcookiesParts=[],
            subName;
        for(subName in subcookies){
            if(subName.length>0&&subcookies.hasOwnProperty(subName)){
                subcookiesParts.push(encodeURIComponent(subName)+"="+
                        encodeURIComponent(subcookies[subName]));
            }
        }
        if(subcookiesParts.length>0){
            cookieText+=subcookiesParts.join("&");
            if(expires instanceof Date){
                cookieText+="; expires="+expires.toGMTString();
            }
            if(path){
                cookieText+="; path="+path;
            }
            if(domain){
                cookieText+="; domain"+domain;
            }
            if(secure){
                cookieText+="; secure";
            }
        }else{
            //设置一个过期时间
            cookieText+="; expires="+(new Date(0)).toGMTString();
        }

        document.cookie=cookieText;
    }

};

注意,上面的所有这些可选参数都是作用于整个cookie本身,而非单个子cookie。 为了在同一个cookie中存储多个子cookie,这些标志都必须一致的。
set方法,第一步是获取指定cookie的名称对应的所有子cookie。 || 可以在当getAll返回null的时候为subcookies设置一个新的对象。 然后在subcookies对象上设置好子cookie值并传给setAll() 接收6个参数,可以设置名值以及其他可选参数。 使用for in遍历subcookies中的属性。注意for in 会枚举包括原型链上的属性,所以加了hasOwnProperty来保证只有实例属性被序列化到子cookie中。
注意 由于可能存在属性名为空字符串的情况,还要检查一下属性名的长度。
将每个子cookie的名值对都存入subcookiesParts数组中,然后用join方法连接并存到cookieText中

删除子cookie

    unset:function(name,subName,path,domain,secure){
        var subcookies=this.getAll(name);//subcookie name
        if(subcookies){
            delete subcookies[subName];
            this.setAll(name,subcookies,null,path,domain,secure);
        }
    },

    unsetAll:function (name,path,domain,secure) {
        // body...
        // new Date(0)
        this.setAll(name,null,new Date(0),path,domain,secure);
    }

注意删除子cookie是不能直接将cookie的失效时间设置为过去的时间。 为了删除一个子cookie必须获取包含在某个cookie中的所有子cookie,然后仅删除需要删除的那个子cookie,然后再将余下的子cookie值保存为cookie的值。
unset 用于删除某个cookie中的单个子cookie
unsetAll用于删除整个cookie

三 cookie的思考

还有一类称为 HTTP专有cookie。 这种cookie只能被从服务器读取,可以在浏览器或者服务器设置,因为JS无法获取HTTP专有cookie的值。
缺点

  • 所有的cookie都会由浏览器作为请求头发送。 cookie信息越大,完成对服务器请求的时间就越长
  • Cookie数量和长度的限制。每个domian最多20条cookie,每个cookie长度最多不超过4KB,所以尽管你使用子cookie还是不能突破这个长度限制。 超过的会被截掉
  • 安全性问题,cookie被人拦截了,它就可以获取所有的session信息。 即使加密也没办法。因为拦截者并不需要知道cookie的意义,他只要原样转发cookie就可以达到目的了。
  • 有些状态不可能保存在客户端。例如,为了防止重复提交表单,我们需要在服务器端保存一个计数器。如果我们把这个计数器保存在客户端,那么它起不到任何作用。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值