zoukankan      html  css  js  c++  java
  • Javascript中关于cookie的那些事儿

    Javascript-cookie

    什么是cookie?

    指某些网站为了辨别用户身份、进行session跟踪而储存在用户本地终端上的数据(通常经过加密)。简单点来说就是:浏览器缓存。

    cookie由什么组成?

      Cookie的形式: Cookie是由name=value形式成对存在的,Cookie字符串必须以分号作为结束符,Cookie除了name属性之外还存在其他4个相关属性。
      设置Cookie的语法如下: set-Cookie:name=value;[expires=date];[path=dir];[domain=domainn];[secure]
      set-Cookie:HTTP请求头文件中set-Cookie响应头字段 上述代码中的方括号的内容是可选属性,只有name属性为必须属性。比如:

    HTTP/1.1 200 OK
    Content-Type:text/html
    Set-Cookie:name=value; expires=Tue, 27-Sep-16 12:11:39 GMT; domain=www.baidu.com; path=/
    

      更多关于HTTP的知识,请戳http://www.cnblogs.com/foodoir/p/5911099.html

      在控制台截图如图:


    Cookie的属性: (以百度为例,如图:)
    name属性
      唯一必须设置的属性,表示Cookie的名称。

    //写入cookie
    document.cookie = "user=陈武";
    alert(document.cookie);
    //本地不存在域名,所以域名为空
    document.cookie = "user="+encodeURIComponent("陈武");
    alert(decodeURIComponent(document.cookie));
    //如果不是向磁盘写入cookie,我们还是可以和获取到cookie的

    expires属性  
      指定Cookie在删除之前要在客户机上保持多长时间,如果不使用该属性,Cookie只对向前浏览器会话有用,当用户关闭浏览器时,Cookie就会自动消失。

    /*
     * 过期时间:到了这个时间点就会自动清理cookie,在会话结束时,就是关闭浏览器后就自动清理cookie了
     * 当火狐浏览器关闭后,火狐的cookie被删除了,但不影响其他浏览器,每个浏览器都保存了自己的cookie,不是通用一个cookie。
     */
    
    //延长时间
    var date = new Date();
    //alert(date.getDate()+1);
    date.setDate(date.getDate()+1);
    //alert(date);
    document.cookie = "user="+encodeURIComponent("陈武")+";expires="+date;
    alert(decodeURIComponent(document.cookie));
    //当过了这个时间点,cookie就自动被清理了,设置为当前的之前事件,则可以删除cookie

      在这里,我们就可以知道,cookie有两种清除方式:1、将date.setDate()设置为之前的时间,代码:date.setDate(date.getDate()+1);2、将document.cookie = "user="+encodeURIComponent("陈武")+";expires="+date;中的date换成newDate(0)=>这里回到的时间是1970年1月1日

    path属性

      决定Cookie对于服务器上的其他网页的可用性,在一般情况下,Cookie对于同一目录下的所有页面都可用。当设置path属性后,Cookie只对指定路径以及子路径的所有网页有效。
      如果/head/index.html 建立了一个cookie,那么在/head/目录里的所有页面,以及该目录下面任何子目录里的页面都可以访问这个cookie。这就是说,在/head/stories/articles 里的任何页面都可以访问/head/index.html建立的cookie。但是,如果/zdnn/ 需要访问/head/index.html设置的cookes,该怎么办?这时,我们要把cookies的path属性设置成“/”。在指定路径的时候,凡是来自同一服务器,URL里有相同路径的所有WEB页面都可以共享cookies。现在看另一个例子:如果想让 /head/filters/ 和/head/stories/共享cookies,就要把path设成“/head”。

    domain属性

      许多服务器都由多台服务器组成,domian属性主要设置相同域的多台服务器共享一个Cookie。
      这里需要注意的有:
        如果定义的是baidu.com,那么这个域名下的任何网页都可以访问,如果定义的是v.baidu.com,只能在这个二级域名访问cookie,主域名和其他域名都不可以访问。
        设置域名必须在当前绑定的服务器上设置,如果在baidu.com服务器上随意设置了其他域名,则无法创建cookie。

    secure属性

      Internet链接本身是不安全的,为保证Internet上的数据安全,会使用SSL协议加密数据并使用安全连接传输数据,一般支持SSL的网站以HTTPS开头,Cookie的secure属性表示Cookie只能通过使用HTTPS或  其他安全协议的Internet链接来传输。如果secure属性不出现,就意味着Cookie在网络上未加密发送。
      secure属性指定必须通过https来访问,仅限加密连接,这样安全性更高。

    HttpOnly

      1、如果在Cookie中设置了"HttpOnly"属性,那么通过后台程序读取,JS脚本将无法读取到Cookie信息,这样能有效的防止XSS攻击。
      2、但是设置HttpOnly属性,Cookie盗窃的威胁并没有彻底消除,因为cookie还是有可能传递的过程中被监听捕获后信息泄漏。

    cookie有哪些操作?
    cookie的常用操作有:读取、操作和删除
      在JavaScript中可以通过document.cookie可以读取当前域名下的cookie,是用分号隔开的键值对构成的字符串。类似于name=aa;age=15;
      注意所有的键值对名称和值都是经过encodeURIComponent()编码的,使用时要进行解码。
      当给document.cookie赋值时,不会直接覆盖现有的cookie,而是会追加一个新的cookie。例如:
        document.cookie="a=1";//执行后会看到新增了一个cookie。
      请看如下的一个例子:(简单的对cookie的获取方法进行了封装)

    function setCookie(name,value,expries,path,domain,secure){
        var cookieName = encodeURIComponent(name)+"="+encodeURIComponent(value);
        if(expries instanceof Date){
            cookieName += ";expries="+expries;
        }
        if(path){
            cookieName += ";path="+path;
        }
        if(domain){
            cookieName += ";path="+domain;
        }
        if(secure){
            cookieName += ";secure";
        }
        document.cookie = cookieName;
    }
    
    //获取cookie
    //function getCookie(){
    //    return document.cookie;
    //}
    //alert(getCookie());
    
    function getCookie(name){
        //cookieName得到user= 或者url= 或者age=
        var cookieName = encodeURIComponent(name)+"=";
        //alert(cookieStart);//结果为user=
        
        //cookieStart得到user= 或者其他的 未知,
        var cookieStart = document.cookie.indexOf(cookieName);
        //alert(cookieStart);//0或者14或者22,不存在就是-1
        
        var cookieValue = null;
        
        if(cookieStart>-1){
            cookieEnd = document.cookie.indexOf(";",cookieStart);
            //alert(cookieEnd);//12(20,-1)
            if(cookieEnd == -1){
                cookieEnd = document.cookie.length;        
            }
            //alert(cookieEnd);//结果为35
            
            //alert(cookieStart + cookieName.length);//结果是5
            cookieValue = decodeURIComponent(document.cookie.substring(cookieStart + cookieName.length,cookieEnd));
            //alert(cookieValue);
            
        }
        return cookieValue;
    }
    alert(getCookie("user"));
    
    //封装一个Date函数
    function setCookieDate(day){
        var date = null;
        if(typeof day == "number" && day>0){
            date = new Date();
            date.setDate(date.getDate()+day);
        }
        else{
            console.error("您传递的参数不合法!必须是数字且大于0!");
        }
        return date;
    }
    
    setCookie('user','foodoir',setCookieDate(1));
    setCookie('age','21',setCookieDate(1));
    setCookie('url','baidu.com',setCookieDate(1));

    事实上我们可以用前面说过的JSON方法(详情请戳:http://www.cnblogs.com/foodoir/p/5894760.html)让我们的代码更简化,设置和删除操作跟读取操作类似,在这里我们同时对cookie进行读取、设置和删除操作,示例代码如下:

    var CookieUtil = {
    //根据key读取cookie
        get: function (name){
             //注意对键编码
            var cookieName = encodeURIComponent(name) + "=",
                cookieStart = document.cookie.indexOf(cookieName),
                cookieValue = null,
                cookieEnd;
            //找到cookie键
            if (cookieStart > -1){
                 //键后面第一个分号位置
                cookieEnd = document.cookie.indexOf(";", cookieStart);
                if (cookieEnd == -1){
                    cookieEnd = document.cookie.length;
                }
                //cookie值解码
                cookieValue = decodeURIComponent(document.cookie.substring(cookieStart + cookieName.length, cookieEnd));
            }
            return cookieValue;
        },
        //设置cookie
        set: function (name, value, expires, path, domain, secure) {
            var cookieText = encodeURIComponent(name) + "=" + encodeURIComponent(value);
            //失效时间,GMT时间格式
            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,保持相同的键、域、路径、安全选项,然后设置失效时间即可
        unset: function (name, path, domain, secure){
            this.set(name, "", new Date(0), path, domain, secure);
        }
    };

    可以像下面使用上面的方法:

    //设置cookie
    CookieUtil.set("name","foodoir");
    CookieUtil.set("age","21");
    
    //读取cookie的值
    alert(CookieUtil.get("name"));//foodoir
    alert(CookieUtil.get("age"));//21
    
    //删除cookie
    CookieUtil.unset("name");
    CookieUtil.unset("age");
    
    //设置cookie,包括它的路径、域和失效日期
    CookieUtil.set("name","fooodoir","abc","www.baidu.com",new Date("January 1,2017"));
    
    //删除刚刚设置的cookie
    CookieUtil.unset("name","abc","www.baidu.com");
    
    //设置安全的cookie
    CookieUtil.set("name","fooodoir",null,null,null,true);

    从上面的例子我们不难看出,cookie的读取、设置和删除操作还是很简单的,那么既然cookie这么好用,那我们就用cookie作为数据库来帮我们来存储数据?

    不不不!关于cookie,我还有话说,WEB开发时cookie大小是受到限制的,下面是我收集到的一些证据:

    一、浏览器允许每个域名所包含的cookie数:
      Microsoft指出InternetExplorer8增加cookie限制为每个域名50个,但IE7似乎也允许每个域名50个cookie。
      Firefox每个域名cookie限制为50个。
      Opera每个域名cookie限制为30个。
      Safari/WebKit貌似没有cookie限制。但是如果cookie很多,则会使header大小超过服务器的处理的限制,会导致错误发生。
      注:“每个域名cookie限制为20个”将不再正确!但是为了兼容低版本,我们尽量将cookie数控制在20个或20个以下。

    二、当很多的cookie被设置,浏览器如何去响应。
      除Safari(可以设置全部cookie,不管数量多少),有两个方法:
      最少最近使用(leastrecentlyused(LRU))的方法:当Cookie已达到限额,自动踢除最老的Cookie,以使给最新的Cookie一些空间。InternetExplorer和Opera使用此方法。
      Firefox很独特:虽然最后的设置的Cookie始终保留,但似乎随机决定哪些cookie被保留没有任何计划。
      建议:在Firefox中不要超过Cookie限制

    三、不同浏览器间cookie总大小也不同:
      Firefox和Safari允许cookie多达4097个字节,包括名(name)、值(value)和等号。
      Opera允许cookie多达4096个字节,包括:名(name)、值(value)和等号。
      InternetExplorer允许cookie多达4095个字节,包括:名(name)、值(value)和等号。
      注意:多字节字符计算为两个字节。在所有浏览器中,任何cookie大小超过限制都被忽略,且永远不会被设置。这样的话,为了兼容低版本,我们尽量控制cookie的大小为4095或4095以下。

    不得不说的几个提高cookie的安全性的几个建议

    一、对保存到cookie里面的敏感信息必须加密

    二、设置HttpOnly为true

      1、该属性值的作用就是防止Cookie值被页面脚本读取。
      2、但是设置HttpOnly属性,HttpOnly属性只是增加了攻击者的难度,Cookie盗窃的威胁并没有彻底消除,因为cookie还是有可能传递的过程中被监听捕获后信息泄漏。

    三、设置Secure为true

      1、给Cookie设置该属性时,只有在https协议下访问的时候,浏览器才会发送该Cookie。
          2、把cookie设置为secure,只保证cookie与WEB服务器之间的数据传输过程加密,而保存在本地的cookie文件并不加密。如果想让本地cookie也加密,得自己加密数据。

    四、给Cookie设置有效期

       1、如果不设置有效期,万一用户获取到用户的Cookie后,就可以一直使用用户身份登录。
         2、在设置Cookie认证的时候,需要加入两个时间,一个是“即使一直在活动,也要失效”的时间,一个是“长时间不活动的失效时间”,并在Web应用中,首先判断两个时间是否已超时,再执行其他操作。

    五、一定不要在cookie中存储重要和敏感的数据

      cookie数据并非存储在一个安全的环境中,其中包含的任何数据都可以被其他人访问到,所以不要在cookie中存储如银行卡或个人地址之类的数据。

    关于子cookie我有话要说

      有时站点需要记录多个cookie,比如多块功能区域都有气泡提示,点击“不再提示”后取消提醒,此时每个区域都需要记录一个很简单的cookie。由于浏览器cookie数量是有限制的,为了减少cookie数量可以使用子cookie的方式。在一个cookie值中使用类似查询字符串的格式可以存储多组键值对,这样就不必每个键值对都占用一个cookie了。子cookie值举例:
      iknow=know0=1&know1=1
      下面来看一个更具体的例子:

    ①获取所有子cookie并将它放在一个对象中返回,对象的属性名为子cookie名称,对象的属性值为子cookie的值。

    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;
                }
                //取出cookie字符串值
                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;
        }

    ②get()获取单个子cookie。

    get: function (name, subName){
             //获取所有子cookie
            var subCookies = this.getAll(name);
            if (subCookies){
                 //从属性中获取单个子cookie
                return subCookies[subName];
            } else {
                return null;
            }
        }

    ③setAll设置整个cookie

    setAll: function(name, subcookies, expires, path, domain, secure){
            var cookieText = encodeURIComponent(name) + "=",
                subcookieParts = new Array(),
                subName;
            //遍历子cookie对象的属性
            for (subName in subcookies){
                 //要先检测属性名
                if (subName.length > 0 && subcookies.hasOwnProperty(subName)){
                     //属性名和属性值编码后=连接为字符串,并放到数组中
                    subcookieParts.push(encodeURIComponent(subName) + "=" + encodeURIComponent(subcookies[subName]));
                }
            }
            if (subcookieParts.length > 0){
                 //用&连接子cookie串
                cookieText += subcookieParts.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();
            }
            //设置整个cookie
            document.cookie = cookieText;
        }

    ④set设置单个子cookie

    set: function (name, subName, value, expires, path, domain, secure) {
            //获取当前cookie对象
            var subcookies = this.getAll(name) || {};
            //单个cookie对应的属性替换
            subcookies[subName] = value;
            //重新设置cookie
            this.setAll(name, subcookies, expires, path, domain, secure);
        }

    ⑤删除cookie
    删除整个cookie, 将失效时间设置为过期日期即可。

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

    删除单个子cookie,需要先获取所有子cookie对象,然后删除子cookie对应的属性,最后再将子cookie对象重新设置回去。

    unset: function (name, subName, path, domain, secure){
             //获取当前cookie对象
            var subcookies = this.getAll(name);
            if (subcookies){
                 //删除子cookie对应的属性
                delete subcookies[subName];
              //重新设置cookie
                this.setAll(name, subcookies, null, path, domain, secure);
            }
        }

    关于cookie我们还需要知道的一些知识

    【在线状态检测】

    开发离线应用时,往往在离线状态时把数据存在本地,而在联机状态时再把数据发送到服务器。html5提供了检测在线状态的方法:navigator.onLine和online/offline事件。
    1.navigator.onLine属性
      表示当前的网络状态是否在线,true表示在线,false表示离线。当网络状态变化时,该属性也会随之变化。
    2.online和offline事件
      HTML5提供了这两个事件,会在网络状态变化时触发。online在网络由离线变为在线时触发;offline在网络由在线变为离线时触发。
      示例代码如下:

    <p>You are currently:
        <span id="status">
            <script>document.write(navigator.onLine ? "在线" : "离线");</script>
        </span>
    </p>
    <p>切换脱机状态,看看效果</p>
    <script>
        EventUtil.addHandler(window, "online", function(){
            document.getElementById("status").innerHTML = "在线";
        });
        EventUtil.addHandler(window, "offline", function(){
            document.getElementById("status").innerHTML = "离线";
        });
    </script>

    知识延伸

    比较古老的存储方式:
        cookie
        userDate(ie5.0)
    html5 存储三种方式
        local storage (永久保存,保存在缓存里。清除方法:手动清除或者浏览器缓存失效)
        application cache
        indexed DB

      (关于其他存储方式这里不作介绍,请大家参考相关资料~)


    参考资料:

      《Javascript高级程序设计(第三版)》第23章;
      《图解HTTP》第8章

    相关知识链接:

      Javascript中关于Cookie的那些事:https://developer.mozilla.org/en-US/docs/Web/API/Document/cookie

      HTTP:http://www.cnblogs.com/foodoir/p/5905946.html

      JSON的那些事儿:http://www.cnblogs.com/foodoir/p/5894760.html

      HTTPS:http://www.cnblogs.com/foodoir/p/5922480.html

      

  • 相关阅读:
    在Objective-C声明Block的几种方式
    属性初始化
    OC协议
    堆排序的OC实现
    iOS 应用性能测试的相关方法、工具及技巧
    墙裂推荐 iOS 资源大全
    剖析@weakify 和 @strongify
    iOS开发大神必备的Xcode插件
    聊聊 KVC 和 KVO 的高阶应用
    TableView的优化
  • 原文地址:https://www.cnblogs.com/foodoir/p/5914631.html
Copyright © 2011-2022 走看看