Blog

Yoki


  • 首页

  • 归档

  • 搜索

使用webp压缩图片(一)

发表于 2018-05-17

什么是 webp

  • 提供了有损压缩和无损压缩的图片格式
  • 谷歌推出的一种图片格式

webp 的目标

  • 减少文件大小,但达到和 jpeg 格式相同的图片品质,希望减少图片在网络上的发送时间。

webp 的 MIME 类型

  • image/webp

推荐阅读

  • webp 适配

浅谈浏览器跨域(二)之最终方案

发表于 2018-05-16

单向跨域(一般用来获取数据)

上面是两个页面之间的交互,现在是单方面向外面聊天的

JSONP

JSON With Padding

  • 原理是 html 的 script 标签可以加载并执行其他域的 js 文件。
  • 一般 query 是 callback,当然是其他也是,只不过现在和服务端约定好了,服务队会获取到 req.query.callback 这样
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<script type="text/javascript">
function dosomething(jsondata){
//处理获得的json数据
}
</script>
<script src="http://example.com/data.php?callback=dosomething"></script>

//服务器
<?php
$callback = $_GET['callback'];//得到回调函数名
$data = array('a','b','c');//要返回的数据
echo $callback.'('.json_encode($data).')';//输出
?>
//服务器会返回domething(['a','b','c'])给浏览器,就可以直接执行了
  • 优点是兼容性好,并且在请求完毕后可以通过调用 callback 的方法回传结果
  • 缺点是支持 get 请求,传输的数据长度有限制
  • 缺点是错误处理机制不完善,没办法进行错误处理
  • 更主要的缺点是安全问题,如果被不信任的服务使用的时候会很危险,比如,callback 参数恶意添加标签(如 script,造成 xss 漏洞)

websocket 协议

  • 是一种通信协议,使用 ws://和 wss(加密)://
  • 这个协议不实行同源策略,只要服务器支持,就可以

通过 CORS 跨域

  • Cross-Origin Resource Sharing 跨域资源共享
  • 规定了浏览器应该如何与服务器如何通过,背后的思想是使用自定义的 http 头部让浏览器和服务器沟通,从而决定请求或者响应是成功还是失败
  • 浏览器不能低于 IE10
  • 对于开发者来说 CORS 通信和同源的 ajax 通信没有差别,代码完全一样。浏览器一旦发现 ajax 请求跨域,就会自动添加一些附加的头部信息,有时还会多处一次附加的请求,但是用户不会有感觉

    实现 CORS 通信的关键是服务器,只要服务器实现了 CORS 接口,就可以跨源通信

1
2
3
4
5
6
7
8
9
10
11
//相对路径是本源
<script type="text/javascript">
var xhr = new XMLHttpRequest(); xhr.open("POST", "/damonare",true);
xhr.send();
</script>
//绝对路径,跨域,无感
<script type="text/javascript">
var xhr = new XMLHttpRequest();
xhr.open("GET", "http://segmentfault.com/u/trigkit4/",true);
xhr.send();
</script>

两种请求

cors 分为两类,简单请求和非简单请求

简单请求

满足下面三个条件,就属于简单请求

  • 请求方法是 get,post,head
  • (Content-Type)只能是 application/x-www-form-urlencoded, multipart/form-data 或 text/plain 中的一种
  • 不会使用自定义请求头(类似于 X-Modified 这种)
基本流程简单
  • 请求的话,浏览器直接发出 CORS 请求。具体就是浏览器发现这次请求是简单请求,就主动在头信息之中,增加一个 origin 字段
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//让我们看看,在这个场景中,浏览器会发送什么的请求到服务器,而服务器又会返回什么给浏览器:
GET /resources/public-data/ HTTP/1.1
Host: bar.other
User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1b3pre) Gecko/20081130
Minefield/3.1b3pre
Accept: text/html,application/xhtml+xml,application/xml;
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Connection: keep-alive
Referer: http://foo.example/examples/access-control/simpleXSInvocation.html
Origin: http://foo.example //该请求来自于 http://foo.exmaple。
//以上是浏览器发送请求

HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 00:23:53 GMT
Server: Apache/2.0.61
Access-Control-Allow-Origin: * //这表明服务器接受来自任何站点的跨站请求。如果设置为http://foo.example。其它站点就不能跨站访问 http://bar.other 的资源了。
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Type: application/xml
//以上是服务器返回信息给浏览器
服务器返回的与 CORS 有关的字段

一般都是以 Access-Control 开头

  • Access-Control-Allow-Origin 接受域名的请求,必须的
  • Access-Control-Allow-Credentials 可选,表示允不允许发送 cookie。如果需要发送,浏览器的 ajax 请求要打开 withCredentials 属性(xhr.withCredentials = true;)
  • Access-Control-Expose-Headers 可选。因为 cors 请求,xhr 对象的 getResponseHeader()方法只能拿到 6 个基本字段:Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma。如果想要拿到其他字段,只能在服务器里面指定,比如 getResponseHeader(‘FooBar’)可以返回 FooBar 字段
非简单请求

非简单请求是对服务器有特殊要求的请求,比如请求方法是 put 和 delete,或者 content-type 是 application/json

  • 会在正式通信之前,增加一次 http 查询请求,称为预检请求
  • 浏览器先询问服务器,当前网页所在的域名是否在服务器的许可名单之中,以及可以使用哪些 HTTP 动词和头信息字段。只有得到肯定答复,浏览器才会发出正式的 XMLHttpRequest 请求,否则就报错
  • 预检请求的方法是 options,表示这个请求是询问的
  • 浏览器发出的预请求
1
2
3
4
5
6
7
8
9
10
11
12
13
14
//浏览器
var url = "http://api.alice.com/cors";
var xhr = new XMLHttpRequest();
xhr.open("PUT", url, true);
xhr.setRequestHeader("X-Custom-Header", "value");
xhr.send();
//http头
OPTIONS /cors HTTP/1.1
Origin: http://api.bob.com
Access-Control-Request-Method: PUT//(必须的)告知请求是什么方法
Access-Control-Request-Headers: X-Custom-Header//发送额外的头部信息,以逗号分割
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
  • 服务器的回应,在检查 origin,method 和 headers 字段后
1
2
3
4
5
6
7
8
9
10
11
12
13
HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:15:39 GMT
Server: Apache/2.0.61 (Unix)
Access-Control-Allow-Origin: http://api.bob.com//(关键)
Access-Control-Allow-Methods: GET, POST, PUT//必须,返回的是所有支持方法,而不但是浏览器请求的那个方法,这样是避免多次预检请求
Access-Control-Allow-Headers: X-Custom-Header//如果浏览器有,则这个字段也是必须的
Access-Control-Max-Age: 1728000 //本次预检请求的有效期,单位是秒,允许缓存该条回应多少秒。在此期间,不用发出另一条预检请求。
Content-Type: text/html; charset=utf-8
Content-Encoding: gzip
Content-Length: 0
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Content-Type: text/plain

一旦服务器通过了预检请求,以后每次浏览器的请求都跟简单请求一样

  • 优点:CORS 的优点:CORS 支持所有类型的 HTTP 请求,是跨域 HTTP 请求的根本解决方案。

浅谈前端跨域(一)

发表于 2018-05-16

什么是跨域

  • 因为有浏览器同源策略限制,当前域名的 js 只能读取同域下的窗口属性
  • 跨域不仅仅是指跨域名。
  • 只要协议,域名,端口有任意不同,都被当作不同的域。

解决跨域问题

主域相同的跨域

  • 如果主域相同,比如 a.com 和 b.a.com,那么可以在 b.a.com 设置
1
document.domain = "a.com";

完全不同源的跨域(两个页面之间的通信)

window.name 跨域(同窗口)

  • window 对象有个属性 name,在一个窗口的生命周期内,窗口载入的所有页面都是共享一个 window.name,每个页面对于 window.name 都有读写的权限
  • window.name 是持久存在一个窗口载入过的所有页面中的。
  • window.name 的神奇之处在于 name 值在不同的页面(甚至不同域名)加载后依旧存在(如果没修改当然就不会变化)
  • 可以支持非常长的 name 值(2mb)

window.postMessage(跨窗口)

  • html 为了解决这个问题,引入了一个全新的 api,跨文档通信 API,它为 window 对象新增了 postMessage 的方法
  • 允许跨窗口通信,不论这个两个窗口是否同源
1
2
3
//父窗口http://aaa.com向子窗口http://bbb.com发消息,调用postMessage方法。
let popup = window.open("http://bbb.com", "title");
popup.postMessage("Hello World!", "http://bbb.com");
  • postMessage 的第一个参数是具体的信息内容,第二个参数是接受消息的窗口的源(origin),也可以设置为‘*’,表示向所有窗口发送
  • 父窗口和子窗口都可以通过 message 事件,监听对方的消息。message 事件的事件对象 event,有三个属性
    • event.source:发送消息的窗口
    • event.origin:消息发向的网址
    • event.data:消息内容
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var onmessage = function(event) {
var data = event.data; //消息
var origin = event.origin; //消息来源地址
var source = event.source; //源Window对象
if (origin == "http://www.aaa.com") {
console.log(data); //hello world!
}
source.postMessage("Nice to see you!", "*");
};
if (typeof window.addEventListener != "undefined") {
window.addEventListener("message", onmessage, false);
} else if (typeof window.attachEvent != "undefined") {
//ie
window.attachEvent("onmessage", onmessage);
}

加密传输之加盐

发表于 2018-05-13

加盐提高安全性

盐:一个随机的字符串,往明文密码和一个随机的字符串拼接在一起

为了应对黑客们用彩虹表破解密码,我们可以先往明文密码加盐,然后再对加盐之后的密码用哈希算法加密。由于盐在密码校验的时候还要用到,因此通常盐和密码的哈希值是存储在一起的。

采用加盐的哈希算法对密码加密,要确保要往每个密码里添加随机的唯一的盐,而不是让所有密码共享一样的盐。

虽然加盐的算法能有效应对彩虹表的破解法,但它的安全级别并不高,因为计算哈希值耗时极短,黑客仍然可以用穷举法来破解,只是增加了一些耗时。

用 BCrypt 或者 PBKDF2 增加破解的难度

为了应对暴力破解法,我们需要非常耗时的而不是非常高效的哈希算法。BCrypt 和 PBKDF2 算法应运而生。

这两个算法最大的特点是我们可以通过参数设置重复计算的次数,重复计算的次数越多耗时越长。如果计算一个哈希值需要耗时 1 秒甚至更多,那么黑客们采用暴利法破解密码将几乎不再可能。破解一个 6 位纯数字密码需要耗时 11.5 天,更不要说高安全级别的密码了。

摘自如何加密传输和存储用户密码

加密传输之HTTPS

发表于 2018-05-13

http 和 https

常规的 http 请求,所有信息明文传播,只要中间人在链路中的任意阶段进行劫持,就会带来三大风险:

  • 窃听风险:第三方可以获取通信内容
  • 篡改风险:第三方可以修改通信内容
  • 冒充风险:第三方可以冒充他人身份参与通信

https 可以解决这些问题

https

http+TLS,TLS 是传输层加密协议,前身是 SSL 协议(1999 年应用广泛,然后标准化,改名就是 TLS,所以同一个东西的不同阶段)

SSL/TLS 协议是为了解决 http 的三大风险而设计的,希望达到:

  • 内容加密:所有信息不再明文传播,都是加密的,第三方无法窃听
  • 数据完整性:防止内容被第三方冒充或者篡改。具有校验机制,一旦被篡改,通信双方会立刻发现。
  • 身份认证:配备身份证书,防止身份被冒充。即使被 DNS 劫持到了第三方站点,也会提醒用户有可能被劫持。

DNS 劫持

又叫域名劫持

  • 是指在劫持的网络范围内拦截域名解析的请求,分析请求的域名

深入理解 http 请求,dns 劫持与解析
dns 劫持和 http 劫持的区别

https 流程

  • 客户端发起 https 请求
  • 服务端的配置
    • 一般需要向权威机构申请一个证书(也可以自己制作,这个会在之后的中间人攻击中讲到,区别就是自己颁发的证书需要客户端验证通过,才可以继续访问,而使用受信任的公司申请的证书则不会提示),证书会生成 RSA 加密使用的一对公钥 A 和私钥 B
  • 传送证书
    • 这个证书主要内容是公钥 A,也包含了其他信息,如证书的颁发机构,过期时间等等
  • 客户端解析证书
    • 由客户端的 TLS 来完成的,主要是验证公钥 A 是否有效,比如颁发机构,过期时间等等,如果发现异常就会弹出一个警告框,提示证书有问题
    • 如果证书没有问题,那么就生成一个随机值(秘钥 D)。之后就进入了不对称加密(RSA)的过程,用证书对该随机值加密(秘钥 D),生成私钥 C
  • 传送加密信息
    • 这部分传送的是用证书加密后的随机值(私钥 C)
  • 服务端解密信息
    • 服务端私钥 B 解密私钥 C 后,得到客户端传过来的秘钥 D,到此 RSA 非对称加密的过程结束,后续所有的数据都可以用私钥 D,进行对称加密和解密
  • 传输加密后的信息
    • 服务端用秘钥 D 加密信息
  • 客户端解密信息
    • 客户端用之前生成的私钥 D 解密服务端传过来的信息,于是获取到了解密后的内容

中间人攻击

https 信息的安全完全建立在证书可信的基础上,如果中间人伪造证书怎么办?

  • 黑客自己伪造的证书需要通过客户端验证通过,才可以继续访问,只要客户端验证通过,那么公钥 A,私钥 B,私钥 C 对黑客来说都是透明的。
  • 所以黑客只要诱导用户安装自己伪造的证书就可以,例如各种钓鱼的不可描述网站

推荐阅读

https 加密过程和 tls 证书验证

加密传输之加密方式

发表于 2018-05-13

加密传输方式

  • 主要有两种,一种是对称加密(想想 shadowsocks),另外一种是非对称加密

对称加密

对称加密算法在加密和解密的时候用的是同一个秘钥

对称加密模式:

  • 甲方选择某一种加密规则,对信息进行加密
  • 乙方使用同一种规则,对信息进行解密

客户端和服务端进行通信,采用对称加密,如果只用一个秘钥,很容易破解;如果每次用不同的,海量秘钥的管理和传输成本又比较高,所以可以采用非对称加密。

非对称加密

需要两个密钥,一个是公钥,一个是私钥

非对称加密模式:

  • 乙方生成两把密钥(公钥和私钥)。公钥是公开的,任何人都可以获得,私钥则是保密的。
  • 甲方获得乙方的公钥后,用它对信息进行加密
  • 乙方拿到加密后的数据后,用私钥进行解密

即使黑客拿到了公钥,没有私钥也是没有办法解密,不考虑彩虹表的情况下,完全可以长期使用一对私钥

什么是彩虹表

彩虹表是一个用于加密散列函数逆运算的预先计算好的表,常用于破解加密过的密码散列。 查找表常常用于包含有限字符固定长度纯文本密码的加密。这是以空间换时间的典型实践,在每一次尝试都计算的暴力破解中使用更少的计算能力和更多的储存空间,但却比简单的每个输入一条散列的翻查表使用更少的储存空间和更多的计算性能。

RSA 算法

最经典的非对称加密算法是 RSA

RSA 公钥加密算法是 1977 年由罗纳德·李维斯特(Ron Rivest)、阿迪·萨莫尔(Adi Shamir)和伦纳德·阿德曼(Leonard Adleman)一起提出的。公钥私钥成对出现,用其中一个加密只能用另一个解密,通常用公钥加密私钥解密。

浅谈前端缓存(二)

发表于 2018-05-13

缓存历史

http1.0 时代

  • 主要通过 Pragma 和 Expires 这两个字段来规范。现在仍然有些古老的客户端不支持 http1.1,所以这个两个头还在使用。

Expires 字段不准确

  • 是服务器返回该请求结果缓存的到期时间,如果再次发出这个请求,客户端的时间小于 Expires 的值,直接使用缓存结果
  • 但是客户端和服务器如果有一方的时间不准确,比如时区,比如客户故意调整系统时间,都会失效

http1.1 时代

  • 主要就是 Cache-Control 来控制了

强缓存

  • 命中状态码是 200,后面会跟着 from cache。现在高版本的 Chrome 换成了 from disk cache(磁盘缓存)和 from memory cache(内存缓存)

内存缓存

  • 快速读取:将编译解析后的文件,直接存入该进程的内存,占据该进程的一定内存资源,以方便下次运行的快速读取
  • 时效性:一旦进程关闭,那么该进程的内存将会被清空

磁盘缓存

  • 直接将缓存写入磁盘文件中,读取缓存需要对缓存存放的硬盘文件进行 I/0 操作,然后重新解析该缓存内容,读取复杂,内容比内存缓存慢。

只有 get 请求能被缓存,post 不可以

  • get 方法是从指定的资源请求数据,post 是向指定资源提交要被处理的数据
  • get 请求可以被缓存,只应当用于取回数据,请求长度有限制
  • post 请求不会被缓存,对数据长度没有要求

用户操作与缓存的关系

  • 当按 F5 进行刷新的时候,会忽略叼 Expires/Cache-control 的设置,会再次发送请求取服务器请求,而 Last-Modified/ETag 还是有效的。就是有可能命中协商缓存。
  • 当按住 ctrl+f5 进行强制刷新的时候,所有缓存机制都将失效
  • 其他一些操作比如,地址栏回车,前进后退都不会影响缓存字段

缓存命中率

  • 缓存命中率:从缓存中得到数据的请求数与所有请求数的比率。理想状态是越高越好。

推荐阅读

node 实践 http 缓存

Http表单的常见编码

发表于 2018-05-11

什么是 http 表单

web 最常见的就是 post 和 get 方法。其中 get 一般将参数编码在 url 中来传递数据,post 或者 put 必须要将数据放在 http 请求实体中,这样的数据就是 http 表单

  • 表单数据的编码方式是 http 头部设置 Content-Type
  • Content-Type 可以是任意 MIME 类型
  • 下面五种最为常见
    • URLencoded:application/x-www-form-urlencoded
    • mutipart:mutipart/form-data
    • json:application/json
    • xml:text/xml
    • 纯文本:text/plain
  • html 的 form 表单支持 urlencoded,mutipart 和 plain text 的编码,通过 enctype 属性来设定。
  • ajax 默认是 json 格式的编码

URLencoded

form 表单的 enctype 默认就是 urlencoded

1
2
3
4
5
6
7
8
9
<form  method='post' enctype='application/x-www-form-urlencoded'>
<input type="text" name='title'>
<input type="text" name='subtitle'>
<input type="submit">
</form>
//http请求头
Content-Type: application/x-www-form-urlencoded
//请求体
title=test&subtitle=%E4%B8%AD%E5%9B%BD=>base64编码(url通用的编码,可以在chrome控制台用decodeURI解码)

JSON

1
2
3
4
5
6
7
$.post('/xxx', {
title: 'test',
content: [1,2,3]
});
Content-Type: application/json;charset=utf-8
//请求实体
{"title":"test","content":[1,2,3]}

XML

1
2
3
4
5
6
7
8
9
10
Content-Type: text/xml
//请求实体
<!--?xml version="1.0"?-->
<methodcall>
<methodname>examples.getStateName</methodname>
<params>
<param>
<value><i4>41</i4></value>
</params>
</methodcall>

ajax使用FormData对象上传文件

发表于 2018-05-11

背景

  • 因为发现用 postman 很轻松的就可以选择 file 类型
  • 但是 ajax 直接 data 提交键值对却是报不合法的
1
2
3
4
5
6
7
8
9
10
//报错的
this.$proxy({
url: "/pgsls/kol/addExcel",
type: "post",
data: {
brandId: "1",
type: "1",
file: file //文件类型
}
});

解决方案

  • 因为要上传类型是文件(二进制数据,而 http 提供的基于文本的通信协议),所以请求头的 Content-Type 要改成 multipart/form-data(默认是 application/x-www-form-urlencoded)
  • input 的 file 类型可以通过 change 事件来获取 file 对象(event.target.files)

HTTP 请求实体

每个字段由一段 boundary string 来分隔,浏览器保证该 boundary string 不与内容重复,因而 multipart/form-data 能够成功编码二进制数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
------WebKitFormBoundaryWyjhf3VZMVeInmDe
Content-Disposition: form-data; name="brandId"

0
------WebKitFormBoundaryWyjhf3VZMVeInmDe
Content-Disposition: form-data; name="type"

1
------WebKitFormBoundaryWyjhf3VZMVeInmDe
Content-Disposition: form-data; name="file"; filename="test.xlsx"
Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet


------WebKitFormBoundaryWyjhf3VZMVeInmDe--
  • 说一下为什么这里文件只有文件名,没有文件路径?
  • 按照浏览器的安全策略,网页是不能获取到一个文件的完整路径。因为网页开发者可能从这个路径获取到其他信息,甚至发起攻击。

实际发送

1
2
3
4
5
6
7
8
9
10
11
12
let formData = new FormData();
formData.append("brandId", this.dialog.selectBrand);
formData.append("type", this.dialog.selectType);
formData.append("file", this.dialog.file);
return this.$proxy({
url: "/pgsls/kol/addExcel",
type: "post",
data: formData,
cache: false,
processData: false,
contentType: false
});
cacah:false
  • cache 设为 false 可以禁止浏览器对该 URL(以及对应的 HTTP 方法)的缓存。 jQuery 通过为 URL 添加一个冗余参数来实现。
  • 该方法只对 GET 和 HEAD 起作用,然而 IE8 会缓存之前的 GET 结果来响应 POST 请求。 这里设置 cache: false 是为了兼容 IE8。
contentType:false
  • jq 的 contentType 默认值是 application/x-www-form-urlencoded,因此传给 data 参数的对象会默认转换为 query string
  • 我们不需要 jq 做这个转换,否则会破坏掉 multipart/form-data 的编码格式。所以设置 false 来禁止 jq 的转换操作
processData:false
  • jq 会将 data 对象转换为字符串来发送 http 请求,默认情况会用 application/x-www-form-urlencoded 编码转换
  • 我们设置 contentType:false 后该转换会失败。因此设置 processData:false 来禁止这个转换过程(相当于双层保险)
  • data 本来就是用 formData 编码好的数据,不需要进行字符串转换

兼容性

  • 由于 formdata 对象是 XMLHttpRequest Level2 接口,需要 IE 10+, Firefox 4.0+, Chrome 7+, Safari 5+, Opera 12+
  • 低版本浏览器只能用 form 表单了

浅谈前端缓存(一)

发表于 2018-05-11

为什么需要前端缓存

  • 重复利用资源,提高网页打开资源
  • 减少 http 请求,减少网络带宽

两种缓存

200(from cache)和 304(not modified)

  • 强缓存:200(from cache)强缓存命中不会发送请求到服务端,直接从本地缓存中获取资源
  • 协商缓存:会发送请求到服务器,服务器通过请求头部字段来验证资源是否命中协商缓存,如果命中,返回 304,通知浏览器从缓存中获取资源

与缓存策略相关的字段

三个字段 Cache-Control,Pragma,Expires(过期时间),下面介绍一下响应头中的

Cache-Control

http1.1 新增的字段,是控制浏览器缓存的主要字段。

  • no-cache:资源可以被缓存,但是立刻过期,下次访问必须验证资源有效性
  • max-age:缓存资源,在指定时间(是 res 里面的 Date 字段)后过期,单位是秒
  • no-store:资源不会被缓存
  • public:资源可以被浏览器和代理服务器缓存
  • private:资源只能被浏览器缓存

Pragma

  • 是 http1.0 的字段,只有一个值 no-cache,功能和 Cache-control:no-cache;一样,但是优先级比他低。

Expires

  • 是缓存到期时间,是一个服务器的绝对时间,由于时区的误差等因素已经较少使用。优先级比 Cache-control:max-age 低。

强缓存命中条件

  • 有些场景下必须避免浏览器缓存,响应头可以这样设置:Cache-Control: no-cache, no-store, must-revalidate(必须验证)
  • 公开资源直接命中强缓存,这是响应最快的。
    • 请求头部不包括 Pragma 字段
    • 响应头部 Cache-Control 中不包括 no-cache、no-store
    • 响应头部 max-age 或者 expire 大于请求日期
  • 如果我们在很短的一段时间内多次访问同一个资源,并且响应头部却没有 max-age 或者 expires 信息,是不是就不会命中强缓存?不是的,浏览器会做出优化,默认会使用一个启发式算法,取响应头(Date-Last-Modified)*0.1 作为缓存的有效时间(其实就相当于 max-age),只要在这段时间内请求这个资源,即使没有缓存过期字段,也会命中强缓存

协商缓存命中条件

Last-Modified/If-Modified-Since

  • 响应头会有一个 Last-Modified,代表这个资源最后修改的时间。
  • 当浏览器再次向服务器请求该资源的时候,会传送 If-Modified-Since(也就是上次服务器传送过来的 Last-Modified),服务器会拿这个值跟本地资源实际最后修改时间作比较,如果文件没有被修改,那么返回 304,通知浏览器从缓存中读取文件。

ETag/If-None-Match

  • ETag 是一个响应首部字段,他是根据资源内容生成的一段 hash 值,标识资源的状态,由服务器产生。
  • 当浏览器再次向服务器端请求这个资源,会带上 If-None-Match(其实就是上次服务器发过来的 ETag 的 hash 值),服务器收到后,会拿这个值和资源实际的 ETag 做比较。
  • 如果一样就命中协商缓存,返回 304

ETag 的优先级比 Last-Modified 高

  • 本来已经有了一个 Last-Modified,为什么还需要 ETag?
  • 这是因为 Last-Modified 只能精确到秒,而如果我们在服务器一秒内多次更改资源,那么将无法识别,还是会命中协商缓存
  • 而且某些服务器不能精确得到文件的最后修改时间
  • 一些文件或许会周期性被更改,但是他内容并没有改变,只是改变了修改时间,这个时候我们并不希望浏览器重新请求这个资源
1…345…9
Yoki

Yoki

云在青天水在瓶

82 日志
21 标签
© 2019 Yoki
由 Hexo 强力驱动
|
主题 — NexT.Pisces v5.1.4