认清cookie!

啥是cookie?

cookie是一小段文本信息,伴随着用户请求在web服务器和浏览器之间传递
用于解决 "如何记录客户端的用户信息"

cookie存在哪里?

cookie 是存在用户硬盘中,用户每次访问站点时,Web应用程序都可以读取 Cookie 包含的信息。当用户再次访问这个站点时,浏览器就会在本地硬盘上查找与该 URL 相关联的 Cookie。如果该 Cookie 存在,浏览器就将它添加到request header的Cookie字段中,与http请求一起发送到该站点。

cookie如何添加?

  • 客户端设置:js提供api -- document.cookie
    ⚠️注意:document.cookiecookie的名和值中不能出现分号、逗号、等号和空格,每一个key之间通过分号和空格来分割。
document.cookie = 'name=value; maxAge=3000; path=/; domain=xx.com; secure'

设置多个cookie
通过document.cookie的方式设置cookie每次只能设置一个,写多个会如何呢?

document.cookie = 'cookie1=value1; cookie2=value2'

尝试一下,会发现只有第一个cookie1设置成功,cookie2被无视,因此设置多个cookie,最简单就是多次调用document.cookie

  • 服务端设置:http响应头
    浏览器发出的有些请求返回头中有Set-Cookie的字段,服务器通过这个字段告知浏览器它需要设置的cookie,然后浏览器检查一下设置的内容是否符合浏览器对cookie的要求,符合条件则设置成功
    ⚠️注意:响应头里set-cookie字段可以有多个,每一个对应一个要设置的cookie,且只能对应一个。
示例

cookie如何获取?

  • 客户端主动获取
    同样是js提供的api -- document.cookie
 var cookies = document.cookie
  • 请求头中携带
    浏览器发送请求时,在请求的头中会自动将“符合条件”的cookie带上。
    既然是请求头中携带的,那么我们通过ajax发送请求的时候,能否顺便设置一下cookie呢?
  var xhr = new XMLHttpRequest();
  xhr.open('GET', url, true);
  xhr.withCredentials = true;
  xhr.setRequestHeader('Cookie', "key=value");
  xhr.send(null);

xhr.withCredentials:

一般跨域请求的时候,比如A域接收B域的请求时,接收不到B域的cookies,也就是说,A服务器的request.getCookies() 为空。解决这个问题,一般把XMLHttpRequest这个对象的withCredentials属性设置为true

实验一下可以发现,我们设置的cookie并没有生效,并且chrome浏览器下我们会看到一行报错

Refused to set unsafe header "Cookie"

因为浏览器的安全限制,我们不能自己随便设置请求头中的cookie。

cookie有哪些属性?

属性名 说明
domain 限制cookie的使用范围,只能在domain值范围内才能访问该cookie
path domain和path共同限制了课访问该cookie的url,如果某个cookie限制了path='/abc',那么只有'domain/abc'下的所有url可以访问该cookie
expires/max-age cookie的过期时间,默认设置为session,即页面关闭后cookie会被清理。
expires 是 http/1.0协议中的选项,在新的http/1.1协议中expires已经由 max-age 选项代替。expires必须是 GMT 格式的时间。
max-age的单位为秒,cookie失效时刻 = 创建时刻 + max-age
httpOnly 如果被标记为httpOnly,那么document.cookie的方式就无法获取到该cookie。同样的,我们通过js来设置cookie的,也无法被标记为httpOnly。
这个值只能通过请求的响应头来设置。默认情况下,cookie不会带httpOnly选项。
secure 对于被标记为Secure的cookie,只有当请求是HTTPS或者其他安全协议时,该cookie 才能被访问到。
同样的,也只有在HTTPS或者其他安全协议时,我们也才能通过js设置secure的cookie。
sameSite 谷歌开发的一种安全机制,该属性表示 Cookie 不随着跨域请求发送,其目的是尝试阻止CSRF。

对于domain值的要求

  • 自身:domain的值可以设置为本身。
  • 向下:所有子域名

如果当前页面的域名是 sub.test.com, 那么 domain 可以设置为.sub.test.com。允许所有sub.test.com的子域名访问,如xx.sub.test.com、xx.xx.sub.test.com。

  • 向上:父级域名,直到顶级域名的下一级

如果当前页面的域名是sub.test.com, 那么domain可以设置为test.com。但是不能设置为.com。因为.com属于Top-Level Domain。

cookie的缺点?

  • 安全性弱:由于cookie在http中是明文传递的,其中包含的数据都可以被他人访问,可能会被篡改、盗用。
  • 大小限制:cookie的大小限制在4kb左右,不适合大量存储。
  • 增加流量:cookie每次请求都会被自动添加到Request Header中,无形中增加了流量。cookie信息越大,对服务器请求的时间越长。

cookie插件

tiny-cookie(vue-cookie)

/*!
 * tiny-cookie - A tiny cookie manipulation plugin
 * https://github.com/Alex1990/tiny-cookie
 * Under the MIT license | (c) Alex Chao
 */

!(function(root, factory) {

  // Uses CommonJS, AMD or browser global to create a jQuery plugin.
  // See: https://github.com/umdjs/umd
  if (typeof define === 'function' && define.amd) {
    // Expose this plugin as an AMD module. Register an anonymous module.
    define(factory);
  } else if (typeof exports === 'object') {
    // Node/CommonJS module
    module.exports = factory();
  } else {
    // Browser globals 
    root.Cookie = factory();
  }

}(this, function() {

  'use strict';

  // The public function which can get/set/remove cookie.
  function Cookie(key, value, opts) {
    if (value === void 0) {
      return Cookie.get(key);
    } else if (value === null) {
      Cookie.remove(key);
    } else {
      Cookie.set(key, value, opts);
    }
  }

  // 检查是否启用了cookie
  Cookie.enabled = function() {
    var key = '__test_key';
    var enabled;

    document.cookie = key + '=1';
    enabled = !!document.cookie;

    if (enabled) Cookie.remove(key);

    return enabled;
  };

  // 根据key名得到cookie的值
  Cookie.get = function(key, raw) {
    if (typeof key !== 'string' || !key) return null;

    key = '(?:^|; )' + escapeRe(key) + '(?:=([^;]*?))?(?:;|$)';

    var reKey = new RegExp(key);
    var res = reKey.exec(document.cookie);

    return res !== null ? (raw ? res[1] : decodeURIComponent(res[1])) : null;
  };

  // 在不解码的情况下获取cookie的值。
  Cookie.getRaw = function(key) {
    return Cookie.get(key, true);
  };

  // 设置cookie的值
  Cookie.set = function(key, value, raw, opts) {
    if (raw !== true) {
      opts = raw;
      raw = false;
    }
    opts = opts ? convert(opts) : convert({});
    var cookie = key + '=' + (raw ? value : encodeURIComponent(value)) + opts;
    document.cookie = cookie;
  };

  // 在不编码的情况下设置cookie的值
  Cookie.setRaw = function(key, value, opts) {
    Cookie.set(key, value, true, opts);
  };

  // 用指定的键删除cookie
  Cookie.remove = function(key) {
    Cookie.set(key, 'a', { expires: new Date() });
  };

  // 工具函数
  // ---------------

  // 转义特殊字符
  function escapeRe(str) {
    return str.replace(/[.*+?^$|[\](){}\\-]/g, '\\$&');
  }

  // 将对象转换为cookie选项字符串。
  function convert(opts) {
    var res = '';

    for (var p in opts) {
      if (opts.hasOwnProperty(p)) {

        if (p === 'expires') {
          var expires = opts[p];
          if (typeof expires !== 'object') {
            expires += typeof expires === 'number' ? 'D' : '';
            expires = computeExpires(expires);
          }
          opts[p] = expires.toUTCString(); // 可根据世界时 (UTC) 把 Date 对象转换为字符串
        }

        if (p === 'secure') {
          if (opts[p]) {
            res += ';' + p;
          }

          continue;
        }

        res += ';' + p + '=' + opts[p];
      }
    }

    if (!opts.hasOwnProperty('path')) {
      res += ';path=/';
    }

    return res;
  }

  // 按给定字符串返回expires
  function computeExpires(str) {
    var expires = new Date();
    var lastCh = str.charAt(str.length - 1);
    var value = parseInt(str, 10);

    switch (lastCh) {
      case 'Y': expires.setFullYear(expires.getFullYear() + value); break;
      case 'M': expires.setMonth(expires.getMonth() + value); break;
      case 'D': expires.setDate(expires.getDate() + value); break;
      case 'h': expires.setHours(expires.getHours() + value); break;
      case 'm': expires.setMinutes(expires.getMinutes() + value); break;
      case 's': expires.setSeconds(expires.getSeconds() + value); break;
      default: expires = new Date(str);
    }

    return expires;
  }

  return Cookie;

}));

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容