浏览器系列 -- 本地存储

823 阅读10分钟

前言

当我们打开浏览器的控制台,可以看到浏览器的本地存储有cookielocalstoragesessionstorage

image.png

cookie 和 localstorage 和 sessionstorage 的区别:

1. 大小上

  • cookie 限制在 4 KB 以内,比较小;
  • localstorage、sessionstorage 都是在 5 MB 以内,比较大;

2. 生命周期上

  • cookie 如果没有设置有效时间,那网页一关闭就没了;如果有设置有效时间,则有效期一到就消失;
  • localstorage 可以是永久的,跟网页、窗口是否关闭无关;

3. 域名限制上

  • 共同点:cookie、localstorage 和 sessionstorage 都遵循同源策略
  • cookie 通过设置 domain 属性可以让同主域下的不同子域拿到父域的 cookie
  • localstorage、sessionstorage 是无法让子域名继承父域名的 localstorage 数据

4. 作用域上

  • 共同点:cookie、localstorage 和 sessionstorage 都不能在不同浏览器之间共享
  • 作用域大小对比:localstorage(不同窗口)> cookie(同一窗口不同Tab)> sessionstorage(同一窗口同一Tab)

5. 网络传输上

  • cookie 会参与网络请求,每次都被携带放在 HTTP 请求头当中
  • localstorage、sessionstorage 不参与网络请求,只在本地和浏览器之间传输
function set(key,value){
    var curtime = new Date().getTime();//获取当前时间
    localStorage.setItem(key,JSON.stringify({val:value,time:curtime}));//转换成json字符串序列
}
function get(key,exp)//exp是设置的过期时间
{
    var val = localStorage.getItem(key);//获取存储的元素
    var dataobj = JSON.parse(val);//解析出json对象
    // 当前时间 - 减去存储的元素在创建时候设置的时间 ? 过期时间
    if(new Date().getTime() - dataobj.time > exp)  console.log("expires");//提示过期
    else  console.log("val=" + dataobj.val); // 未过期,拿出这个值
}

6. 应用场景上

  • cookie 用于存个性化主题、账号、密码、token
  • localstorage 可以用于存储一些永久的数据,如用户的注册时间
  • sessionstorage 可以用于存储临时购物车数据

cookie

cookie 是什么

cookie是浏览器管理状态的一个文件,可以用来存储一些数据

cookie 的存储内容

  1. 会话状态管理:用户登录状态账号信息游戏分数购物车收藏夹
  2. 用户行为分析:访问次数停留时长
  3. 个性化设置:用户自定义主题

cookie 各属性(字段)解读

image.png 我们从该图可以观察到cookie的属性(字段)有name、value、domain、path、expires、max-age、size、HttpOnly、Samesite、Secure、Priority

name (cookie的名字)

这个显而易见,就是代表cookie的名字的意思,一个域名下绑定的cookie,name不能相同,否则相同的name的值会被覆盖掉

value (cookie的值)

这个就是每个cookie拥有的一个属性,它表示cookie的值

由于cookie规定是名称/值是`不允许包含分号,逗号,空格的`
所以为了不给用户到来麻烦,考虑服务器的兼容性,任何存储cookie的数据都应该被编码

// 例如将cookie中name为 tz 设置为 广东;shenzhen, 有中文也有分号, 就需要编码
document.cookie=`tz=${encodeURIComponent('广东;shenzhen')}`

console.log(document.cookie)
// tz=%E5%B9%BF%E4%B8%9C%3Bshenzhen

console.log(decodeURIComponent(document.cookie))
// tz=广东;shenzhen

domain (针对主域相同、子域不同的情况)

domain表示的是cookie绑定的域名

domain 应用场景:跨子域

  • www.baidu.com 和 image.baidu.com 是属于同主域下的不同子域的两个网站,不满足同源策略,所以不能共享cookie
  • 但如果这两个网站的cookie的domain属性都设置为.baidu.com,那么所有以".baidu.com"结尾的域名网站都可以共享该Cookie

path(同域下不同路径的文件)

path表示 cookie所在的目录

  • cookie规定:path路径下的页面可以访问,path以上或以外路径的页面不可以访问

举例:

  1. 在同一个服务器上有目录如下:/test/test/1/test/2
  2. cookie1的path为/test/, cookie2的path为/test/1
  3. test下的所有页面都可以访问到cookie1,/test/和/test/2的子页面不能访问cookie2

expires/max-age

image.png

  • expires表示[过期时间],一个具体的时间字符串,但已经逐渐被max-age所替代,原因有:
    1. 【格式问题】expires属性首先需要将时间日期转换为GMT/UTC格式,否则某些浏览器可能会出现误差
    2. 【时间差】浏览器与服务器之间的时间可能存在时间差,因此也会造成出错
  • max-age表示[有效期],采用max-age需要注意的点:
    1. max-age的值是一个数字,单位是(s),表示这个cookie的有效时间
    2. 兼容性问题:目前chrome、Firefox等现代浏览器是支持max-age的,但IE是不支持max-age的 那如何解决兼容性问题又保证不出差错呢?

我想到的方法是:根据max-age设置cookie的过期时间,注意:服务端传过来的是max-age,但前端存的是expires,所以需要换算一下

function setCookie(name,value,maxage){
    var date = new Date()
    date.setDate(date.getTime()+maxage*1000)
    document.cookie="name=" + name + "; expires=" + date + "; path=/; domain=.baidu.com"
}

secure(只在 HTTPS 下传输)

image.png

  • 这个属性译为安全,http是不安全的协议,容易被劫持,当这个属性设置为true时,此cookie只会httpsssl等安全协议下传输
  • 这个属性并不能对客户端的cookie进行加密,不能保证绝对的安全性,仍然可能被窃取或冒用 HTTP和HTTPS的详细内容见 计算机网络系列 -- HTTP计算机网络系列 -- HTTPS

HttpOnly(只在 http 请求中携带但不能读写)

如果这个属性设置为 true,则只有在 http 请求头中才会携带此 cookie 的信息,但不能通过document.cookie 来访问此cookie,能有效的防止 XSS 攻击

SameSite(不认第三方 cookie、只认第一方cookie)

Chrome 51 开始,浏览器的 Cookie 新增加了一个SameSite属性,用来防止 CSRF 攻击和用户追踪

  • Strict [最严格]
Strict最为严格,完全`禁止第三方 Cookie`,跨站点时,`任何情况下`都不会发送 Cookie。
换言之,只有当前网页的 URL 与请求目标`一致`,才会带上 Cookie
  • Lax [稍放宽] 【默认值】
大多数情况也是不发送第三方 Cookie,但是导航到`目标网址的 Get 请求`除外

导航到目标网址的 GET 请求,只包括三种情况:链接预加载请求GET 表单

image.png

  • None [完全开启]
同站请求、跨站请求都会携带cookie了 意味着有可能会受到攻击

cookie 的特点

1. 有保质期

  • 会话 cookie:没有指定ExpiresMax-Age 指令,其Expires/Max-Age字段的值为Session
  • “永久” cookie:有指定ExpiresMax-Age 指令,其Expires/Max-Age字段的值为一个具体时间字符串 image.png

2. 满足同源策略

  • 同源策略下的网站共享 cookie
  • 不满足同源策略下的网站无法共享 cookie 这里的“共享”是指获取、更改,但不包括请求时被携带
满足同源策略下:在`path`指定的`路径及其下目录`(子网页的意思)的页面都可以获取,否则不可以获取
不满足同源策略下:在`domain`指定的`主域下的所有子域`可以获取,否则就不可以获取

为什么说 不包括请求时被携带,又为什么 cookie不可跨域 还会 遭受CSRF攻击 呢?

这是因为:网站B可以想网站A发送任何一个请求,而这个http请求中的请求头是`可以携带`网站A的cookie的
因为浏览器是`看请求域名携带cookie的`CSRF攻击就是利用这个漏洞),这与cookie不可跨域`并不冲突`
`cookie不可跨域`是指:在网站B下,只能读取网站B的cookie而不能读取网站A的cookie

3. 内存大小受限

Cookie有个数和大小的限制,大小一般是4k

image.png

4. 安全性不高

cookie 在本地可以被更改,也可以被获取,也可以被冒用 敏感的数据不要放在cookie里

同一个域名下的所有请求,都会自动携带 Cookie

cookie 的缺点

  • 容量方面:有上限,最大只能有 4KB
  • 性能方面:同一个域名下的所有请求,都会携带 Cookie,某些请求(a,img,link等标签发出的请求)可能不需要此cookie,会加大传输的头部,损耗一定时空开销
  • 安全问题:客户端可以通过一定手段(脚本,devtools,本地存储的文件,修改host文件)获取到,存在XSS,CSRF等安全问题

解决安全问题的方案

  1. 减短cookie的有效时间
  2. 设置HttpOnly属性:防止本地脚本读取cookie,可防止 XSS 攻击
  3. 设置samesite属性:严格卡控cookie的携带范围,可防止 CSRF 攻击
  4. 服务端对传送的cookie加密
  5. 添加Secure属性:使用https协议传输 前端安全的详细内容具体见 浏览器系列 -- 前端安全

cookie 存放的位置

会话cookie:存放在浏览器内存当中 永久cookie:存放在本地硬盘当中

比如:Chrome浏览器的cookie就保存在C:\Users\Administrator\AppData\Local\Google\Chrome\User Data\Default\Cookies.txt

cookie 的创建及使用

image.png

服务端下发cookie

第一次访问网站的时候,浏览器发出请求,服务器响应请求后,会将cookie放入到响应请求中

image.png

前端创建cookie

前端收到服务器发送过来的HTTP报文后

// 以报文里Set-Cookie里的内容为准设置cookie,该函数可以封装起来
function setCookie(name,value,maxage){
    var date = new Date()
    date.setDate(date.getTime()+maxage*1000)
    document.cookie="name=" + name + "; expires=" + date + "; path=/; domain=.baidu.com"
}

这里注意,全部cookie是一个字符串,每个cookie之间用"; "(分号+空格)隔开

image.png

前端获取 cookie

浏览器第二次请求时会携带cookie

1. 前端JavaScript先获取cookie里的内容
如:var x = document.cookie;
JavaScript中写上述代码即可读取,注意返回的是全部cookie组成的一长串字符串
2. 从字符串中读取`expires/max-age`的值来判断当前cookie是否有效
// 将字符串形式转化为object形式并取值
function cookie(name){    
    var cookieArray = document.cookie.split("; "); //得到分割的cookie名值对
    var cookie = new Object(); 
    for (var i=0;i<cookieArray.length;i++){ 
        var arr = cookieArray[i].split("="); //将名和值分开 
        if(arr[0]==name) return decodeURIComponent(arr[1]); //如果是指定的cookie,则返回它的值
    } 
    return ""; 
} 

这里注意:当使用encodeURIComponent()编码后,在取出值以后需要使用decodeURIComponent()进行解码才能得到原来的cookie值

前端更新 cookie

我们是通过服务端的Set-Cookie对cookie进行增删改的,所以服务端每次更新cookie,前端都要同步更新

JavaScript 在设置 cookie 时会遵循这样一条原则:新设置的 cookie 如果与原有的 cookie 相同(名称、路径和域名都相同),会将原有的 cookie 覆盖

根据这条原则我们更新cookie的时候注意指明:要修改cookie的 名称、路径和域名

前端删除cookie

document.cookie="expires=Thu, 01 Jan 1970 00:00:00 GMT";
// 设置过期时间为过去的时间即可
document.cookie="max-age=0";
// 设置有效期为0也可以
document.cookie="max-age=-1"; 【max-age的默认值是-1,这也就对应了没有设置expires和max-age属性就cookie默认为会话cookie】
// 设置为负数表示让cookie变成会话cookie,浏览器一关对应的cookie就没了

参考文章