1 Star 3 Fork 0

Jay_Ohhh / front-end notes

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
后台和网络.md 72.62 KB
一键复制 编辑 原始数据 按行查看 历史
Jay_Ohhh 提交于 2022-07-11 12:37 . update

Ajax

ajax封装promise
// 原生ajax
// ajax更多内容(方法、参数等等)请到W3school查看
function myAjax(url) {
  return new Promise((resolve, reject) => {
    // 1、创建XMLHttpRequest对象(简称XHR)
    // 前者 for IE7+, Firefox, Chrome, Opera, Safari
    // 后者 for IE6, IE5
    var XHR = window.XMLHttpRequest
      ? new XMLHttpRequest()
      : new ActiveXObject('Microsoft.XMLHTTP')

    // 2、建立链接
    /*
          参数:
              1、method:请求的类型;GET 或 POST
                *GET:请求参数在url后边拼接,send方法为空参
                *POST:请求参数在send方法中定义,支持多种格式,详细请查看这个网址
                 https://blog.csdn.net/hsl0530hsl/article/details/88558353
                请求无副作用时(如进行搜索),便可使用GET方法
                请求有副作用时(如添加数据行),则用POST方法
              2、url:所请求的文件在服务器上的位置
              3、async:true(异步,默认值)或 false(同步)
      */
    XHR.open('GET', url, true)

    // post 方式发送数据 需要设置请求头
    // XHR.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')

    // xhr.responseType:设置返回数据的类型

    // 3、发送请求
    XHR.send()

    // xhr.onload()只有状态码为4时才能回调一次函数。
    // xhr.onprogress(),也就是当状态码为3时(在请求完成之前),会周期性执行这个函数。

    // 4、接受并处理来自服务器的响应结果
    // 当XHR对象的就绪状改变就触发
    XHR.onreadystatechange = function () {
      // readyState == 4 :请求已完成,且响应已就绪
      // XHR.status == 200 :'请求成功'
      if (XHR.readyState == 4 && XHR.status == 200) {
        // responseText 获得字符串形式的响应数据
        resolve(JSON.parse(XHR.responseText))
      } else if (XHR.status >= 400) {
        reject('发生错误')
      }
    }
  })
}

同源政策

同协议、同域名、同端口。

当一个请求url的协议、域名、端口三者之间任意一个与当前页面url不同即为跨域:

当前页面url 被请求页面url 是否跨域 原因
http://www.test.com/ http://www.test.com/index.html 同源(协议、域名、端口号相同)
http://www.test.com/ https://www.test.com/index.html 跨域 协议不同(http/https)
http://www.test.com/ http://www.baidu.com/ 跨域 主域名不同(test/baidu)
http://www.test.com/ http://blog.test.com/ 跨域 子域名不同(www/blog)
http://www.test.com:8080/ http://www.test.com:7001/ 跨域 端口号不同(8080/7001)

常见跨域方式

https://mp.weixin.qq.com/s/OKDKf2bg61zyqx94z7CdTg

https://www.cnblogs.com/sdcs/p/8484905.html

  1. jsonp;

  2. postMassage,html5新增接口,多窗口中的信息传递;

  3. 跨域资源共享CORS,服务器端Access-Control-Allow-Origin设置可以通过的源,前端页面如果需要接收服务器带回的Cookie信息,需要打开xhr.withCredentials = true;确定请求是否携带Cookie;

    普通跨域请求:只服务端设置Access-Control-Allow-Origin即可,前端无须设置,若要带cookie请求:前后端都需要设置。

  4. nginx反向代理;

  5. nodejs中间件代理跨域;

  6. WebSocket协议跨域

  7. document.domain + iframe,二级域名和顶级域名的资源共享,两个页面设置同样的document.domain属性可以实现跨域;(现在谷歌浏览器对domain有限制了)

  8. window.name + iframe,页面中打开页面后window.name任然存在且可以访问;

  9. location.hash + iframe

JSONP

JSONP 可以跨域请求数据,它的原理主要是利用了script标签的src可以跨域链接资源的特性,同时它还可以将服务器端返回的响应数据当做JS代码执行。

JSONP 只支持get,因为script标签只能使用get请求。

jsonp的原理如下:

<script type="text/javascript">
    function foo(data){
       console.log(data)
    }
</script>
<script type="text/javascript" src="xxx"></script>

1、页面全局定义一个处理数据的函数,必须写在第2步的script标签前

2、将非同源服务器端地址或者外部JS文件地址写在script标签的src(这个script标签通常是动态生成),服务器端返回的内容如下:

foo({"name":"tom","age":18});

3、src加载完资源,执行代码。

动态生成 script 标签:

var script = document.createElement('script');
script.type = 'text/javascript';
// 传参并指定回调执行函数为onBack
script.src = 'http://www.demo2.com:8080/login?user=admin&callback=onBack';
document.body.appendChild(script);
// onload事件在资源被加载完成后会被触发,因为外部JS文件加载后会立即放到执行栈执行,删除script元素本身不会影响。
script.onload = ()=>{
  document.body.removeChild(script)
}
CORS

http://www.ruanyifeng.com/blog/2016/04/cors.html

"跨域资源共享"(Cross-origin resource sharing),CORS需要浏览器和服务器同时支持。

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

对于简单请求

浏览器直接发出CORS请求。具体来说,就是在头信息之中,增加一个Origin字段。

下面是一个例子,浏览器发现这次跨源AJAX请求是简单请求,就自动在头信息之中,添加一个Origin字段。

GET /cors HTTP/1.1
Origin: http://api.bob.com
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...

上面的头信息中,Origin字段用来说明,本次请求来自哪个源(协议 + 域名 + 端口)。服务器根据这个值,决定是否同意这次请求。

如果Origin指定的源,不在许可范围内,服务器会返回一个正常的HTTP回应。浏览器发现,这个回应的头信息没有包含Access-Control-Allow-Origin字段(详见下文),就知道出错了,从而抛出一个错误,被XMLHttpRequestonerror回调函数捕获。注意,这种错误无法通过状态码识别,因为HTTP回应的状态码有可能是200。

如果Origin指定的域名在许可范围内,服务器返回的响应,会多出几个头信息字段。

Access-Control-Allow-Origin: http://api.bob.com
Access-Control-Allow-Credentials: true
Access-Control-Expose-Headers: FooBar
Content-Type: text/html; charset=utf-8

上面的头信息之中,有三个与CORS请求相关的字段,都以Access-Control-开头,是服务器端根据浏览器请求的 Origin字段的值,接受请求后在响应头添加的字段:

(1)Access-Control-Allow-Origin

该字段是必须的。它的值要么是请求时Origin字段的值,要么是一个*,表示接受任意域名的请求。

(2)Access-Control-Allow-Credentials

该字段可选。它的值是一个布尔值,表示是否允许发送Cookie。默认情况下,Cookie不包括在CORS请求之中。设为true,即表示服务器明确许可,Cookie可以包含在请求中,一起发给服务器。这个值也只能设为true,如果服务器不要浏览器发送Cookie,删除该字段即可。

(3)Access-Control-Expose-Headers

该字段可选。CORS请求时,XMLHttpRequest对象的getResponseHeader()方法只能拿到6个基本字段:Cache-ControlContent-LanguageContent-TypeExpiresLast-ModifiedPragma。如果想拿到其他字段,就必须在Access-Control-Expose-Headers里面指定。上面的例子指定,getResponseHeader('FooBar')可以返回FooBar字段的值。

withCredentials 属性

上面说到,CORS请求默认不发送Cookie和HTTP认证信息。如果要把Cookie发到服务器,一方面要服务器同意,指定Access-Control-Allow-Credentials字段。

Access-Control-Allow-Credentials: true

另一方面,开发者必须在AJAX请求中打开withCredentials属性。

var xhr = new XMLHttpRequest();
xhr.withCredentials = true;

否则,即使服务器同意发送Cookie,浏览器也不会发送。或者,服务器要求设置Cookie,浏览器也不会处理。

但是,如果省略withCredentials设置,有的浏览器还是会一起发送Cookie。这时,可以显式关闭withCredentials

xhr.withCredentials = false;

需要注意的是,如果要发送Cookie,Access-Control-Allow-Origin就不能设为星号,必须指定明确的、与请求网页一致的域名。同时,Cookie依然遵循同源政策,只有用服务器域名设置的Cookie才会上传,其他域名的Cookie并不会上传,且(跨源)原网页代码中的document.cookie也无法读取服务器域名下的Cookie。

服务器设置CORS

https://blog.csdn.net/weixin_45368324/article/details/105494901

// 允许cors跨域;
ctx.set("Access-Control-Allow-Origin", "http://localhost:3000");
// 允许前端设置的头部信息;(请求头)
ctx.set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Authorization,test");
// 允许前端获取的头部信息;(响应头)
ctx.set("Access-Control-Expose-Headers", "Content-Type, Content-Length,Date")
// 允许前端请求的方法;
ctx.set("Access-Control-Allow-Methods", "GET,POST,PUT,DELETE,HEAD,OPTIONS");
// 允许携带凭证:cookie
ctx.set("Access-Control-Allow-Credentials", true);
// 设置预检请求的缓存时间
ctx.set("Access-Control-Max-Age", 3600 * 24);

跨域的请求在服务端会不会真正执行?

https://mp.weixin.qq.com/s/-ahsfKWbRZq8U7epjJGk_w

  • 简单请求:不管是否跨域,只要发出去了,一定会到达服务端并被执行,浏览器只会隐藏返回值
  • 复杂请求:先发options预检,预检不会真正执行业务逻辑,预检通过后才会发送真正请求并在服务端被执行

状态码

分类
分类 分类描述
1** 信息提示(服务器收到请求但需要请求者继续执行操作)
2** 成功(操作被成功接收并处理)
3** 重定向(需要进一步的操作以完成请求)
4** 客户端错误(请求包含语法错误或无法完成请求)
5** 服务器错误(服务器在处理请求的过程中发生了错误)
数字 英文名称 中文描述
100 Continue 继续(应继续请求)
101 Switching Protocols 切换协议(服务器根据客户端的请求切换协议)
200 OK 请求成功(一般用于GET与POST请求)
201 Created 已创建(成功请求并创建了新的资源)
202 Accepted 已接受(接收请求但未处理完成)
203 Non-Authoritative Information 非授权信息(请求成功但返回的meta信息不在原始的服务器)
204 No Content 无内容(服务器成功处理但未返回内容)
205 Reset Content 重置内容(服务器处理成功且用户终端(例如:浏览器)应重置文档视图)
206 Partial Content 部分内容(服务器成功处理了部分GET请求)
300 Multiple Choices 多种选择
301 Moved Permanently 永久移动(请求的资源已被永久的移动到新URI但返回信息会包括新的URI且浏览器会自动定向到新URI
302 Found 临时移动(但源只是临时被移动)
303 See Other 查看其它地址(使用GET和POST请求查看)
304 Not Modified 未修改(协商缓存时资源未修改)
305 Use Proxy 使用代理
307 Temporary Redirect 临时重定向
400 Bad Request 客户端请求的语法错误(服务器无法理解)
401 Unauthorized 请求要求用户的身份认证
402 Payment Required 保留(将来使用)
403 Forbidden 服务器理解请求客户端的请求(但是拒绝执行此请求)
404 Not Found 服务器无法根据客户端的请求找到资源(网页)
405 Method Not Allowed 客户端请求中的方法被禁止
406 Not Acceptable 服务器无法根据客户端请求的内容特性完成请求
407 Proxy Authentication Required 请求要求代理的身份认证
408 Request Time-out 服务器等待客户端发送的请求时间过长
409 Conflict 服务器完成客户端的 PUT 请求时可能返回此代码
410 Gone 客户端请求的资源已经不存在
411 Length Required 服务器无法处理客户端发送的不带Content-Length的请求信息
412 Precondition Failed 客户端请求信息的先决条件错误
413 Request Entity Too Large 由于请求的实体过大
414 Request-URI Too Large 请求URL过长(URI通常为网址)
415 Unsupported Media Type 服务器无法处理请求附带的媒体格式
416 Requested range not satisfiable 客户端请求的范围无效
417 Expectation Failed 服务器无法满足Expect的请求头信息
500 Internal Server Error 服务器内部错误
501 Not Implemented 服务器不支持请求的功能
502 Bad Gateway 从远程服务器接收到了一个无效的响应
503 Service Unavailable 由于超载或系统维护(服务器暂时的无法处理客户端的请求且延时的长度可包含在服务器的Retry-After头信息中)
504 Gateway Time-out 充当网关或代理的服务器(未及时从远端服务器获取请求)
505 HTTP Version not supported 服务器不支持请求的HTTP协议的版本

http缓存

https://www.jianshu.com/p/256d0873c398

https://mp.weixin.qq.com/s/0P8_lnVf2_zMzIBJ20qajA

前端缓存主要是分为HTTP缓存和浏览器缓存。其中HTTP缓存是在HTTP请求传输时用到的缓存,主要在服务器代码上设置;而浏览器缓存则主要由前端开发在前端js上进行设置。

浏览器与服务器通信的方式为应答模式,即是:浏览器发起HTTP请求 – 服务器响应该请求。那么浏览器第一次向服务器发起该请求后拿到请求结果,会根据响应报文中HTTP头的缓存标识,决定是否缓存结果,是则将请求结果和缓存标识存入浏览器缓存中,简单的过程如下图:

(强制和协商缓存的标识都是在响应报文的HTTP头中和请求结果一起返回给浏览器)

强制缓存

强制缓存就是向浏览器缓存查找该请求结果,并根据该结果的缓存规则来决定是否使用该缓存结果的过程,强制缓存的情况有三种:

强制缓存直接减少请求数,是提升最大的缓存策略。

1、不存在该缓存结果和缓存标识,强制缓存失效,则直接向服务器发起请求(跟第一次发起请求一致),如下图:

2、存在该缓存结果和缓存标识,但该结果已失效,强制缓存失效,则使用协商缓存,如下图:

3、存在该缓存结果和缓存标识,且该结果尚未失效,强制缓存生效,直接返回该结果,如下图:

强缓存规则

当浏览器向服务器发起请求时,服务器会将缓存规则放入HTTP响应报文的HTTP头中和请求结果一起返回给浏览器,告诉浏览器当前资源的缓存规则,控制强缓存的响应头字段分别是Cache-ControlExpires,其中Cache-Control优先级比Expires高。如果当前资源被缓存,则浏览器在下次发起请求时会从缓存中寻找资源。

  • Expires:

    • HTTP1.0
    • 由于是绝对时间,用户可能会将客户端本地的时间进行修改,而导致浏览器判断缓存失效,重新请求该资源。此外,即使不考虑自信修改,时差或者误差等因素也可能造成客户端与服务端的时间不一致,致使缓存失效。
    • 写法太复杂了。表示时间的字符串多个空格,少个字母,都会导致非法属性从而设置失效
  • Cache-control

    • HTTP1.1

    • 优先级高

    • max-age:即最大有效时间,单位秒

      must-revalidate:如果超过了 max-age 的时间,浏览器必须向服务器发送请求,验证资源是否还有效。

      no-cache:虽然字面意思是“不要缓存”,但实际上还是要求客户端缓存内容的,只是是否使用这个内容由后续的对比来决定。

      no-store: 真正意义上的“不要缓存”。所有内容都不走缓存,包括强制和对比。

      public:所有的内容都可以被缓存 (包括客户端和代理服务器, 如 CDN)

      private:所有的内容只有客户端才可以缓存,代理服务器不能缓存。默认值。

协商缓存

控制强制缓存的响应头字段分别是 EtagLast-Modified,请求头字段是 If-None-MatchIf-Modified-Since

每次请求需要进行缓存新鲜度校验。新鲜度校验通过请求头 If-None-Match 与响应头 ETag 进行对比,或请求头 If-Modified-Since 与响应头 Last-Modified 进行对比。

协商缓存就是强制缓存失效后(强制缓存时请求的缓存结果失效,只返回缓存标识),浏览器携带缓存标识向服务器发起请求,由服务器根据缓存标识决定是否使用缓存的过程,主要有以下两种情况:

协商缓存请求数上和没有缓存是一致的,但如果是 304 的话,返回的仅仅是一个状态码而已,并没有实际的文件内容,因此 在响应体体积上的节省是它的优化点

一般来说,我们对经构建工具打包后带有hash的资源进行一年的强制缓存 Cache-Control: max-age=3153600

对不带hash的资源进行协商缓存或Cache-Control: max-age=较小值Cache-Control: no-cache

一种缓存策略是 index.html 适合走协商缓存,文件名带hash | 相对稳定 | 不常更新 的静态资源(JS、CSS、IMAGES) 等使用强缓存。

Etag/If-None-Match:

协商缓存 图3

Etag:

Etag是属于HTTP 1.1属性,它是由服务器(Apache或者其他工具)生成保存并返回给前端并,用来帮助服务器控制Web端的缓存验证。

Etag 的优先级高于 Last-Modified

If-None-Match:

如果If-None-Match传递给后台的缓存ETag值和后台对应该文件的ETag值一样,说明该缓存新鲜,服务器返回 304 状态码,浏览器使用本地缓存;

如果If-None-Match传递给后台的缓存ETag值和后台对应该文件的ETag值不一样,说明该缓存不新鲜,服务器返回更新的资源和新的 Etag值;

Last-Modifed/If-Modified-Since:

Last-Modified:

协商缓存 图2

  • 服务器通过 Last-Modified 字段告知客户端,资源最后一次被修改的时间
  • 浏览器将这个值和内容一起记录在缓存数据库中。
  • 下一次请求相同资源时时,浏览器从自己的缓存中找出“不确定是否过期的”缓存。因此在请求头中将上次的 Last-Modified 的值写入到请求头的 If-Modified-Since 字段
  • 服务器会将 If-Modified-Since 的值与 Last-Modified 字段进行对比。如果相等,则表示未修改,响应 304;反之,则表示修改了,响应 200 状态码,并返回数据。
  • 如果资源更新的速度是秒以下单位,那么该缓存是不能被使用的,因为它的时间单位最低是秒。
  • 如果文件是通过服务器动态生成的,那么该方法的更新时间永远是生成的时间,尽管文件可能没有变化,所以起不到缓存的作用。

If-Modified-Since:

Last-Modified + If-Modified-Since: 如果If-Modified-Since就是 服务器端该文件的最新修改日期,说明缓存是新鲜的,服务器返回304,浏览器使用本地缓存; 如果If-Modified-Since小于 服务器端该文件的最新修改日期,说明缓存不新鲜,服务器返回新的该资源,并且更新该资源Last-Modified日期

  • Last-Modifed/If-Modified-Since的时间精度是秒,而Etag可以更精确。
  • Etag优先级是高于Last-Modifed的,所以服务器会优先验证Etag

1、协商缓存生效,返回304,如下:

2、协商缓存失效,返回200和请求结果结果,如下:

刷新页面跳过缓存
  • ctrl+f5 强制刷新网页时,直接从服务器加载,跳过强缓存和协商缓存;
  • f5刷新网页时,跳过强缓存,但是会检查协商缓存;
总流程

HTTP 常用请求头

可以将http首部分为通用首部,请求首部,响应首部,实体首部

协议头 说明
Accept 可接受的响应内容类型(Content-Types)。
Accept-Charset 可接受的字符集
Accept-Encoding 可接受的响应内容的编码方式。
Accept-Language 可接受的响应内容语言列表。
Accept-Datetime 可接受的按照时间来表示的响应内容版本
Authorization 用于表示HTTP协议中需要认证资源的认证信息
Cache-Control 用来指定当前的请求/回复中的,是否使用缓存机制。
Connection 客户端(浏览器)想要优先使用的连接类型
Cookie 由之前服务器通过Set-Cookie(见下文)设置的一个HTTP协议Cookie
Content-Length 以8进制表示的请求体的长度
Content-MD5 请求体的内容的二进制 MD5 散列值(数字签名),以 Base64 编码的结果
Content-Type 请求体的MIME类型 (用于POST和PUT请求中)
Date 发送该消息的日期和时间(以RFC 7231中定义的"HTTP日期"格式来发送)
Expect 表示客户端要求服务器做出特定的行为
From 发起此请求的用户的邮件地址
Host 表示服务器的域名以及服务器所监听的端口号。如果所请求的端口是对应的服务的标准端口(80),则端口号可以省略。
If-Match 仅当客户端提供的实体与服务器上对应的实体相匹配时,才进行对应的操作。主要用于像 PUT 这样的方法中,仅当从用户上次更新某个资源后,该资源未被修改的情况下,才更新该资源。
If-Modified-Since 允许在对应的资源未被修改的情况下返回304未修改
If-None-Match 允许在对应的内容未被修改的情况下返回304未修改( 304 Not Modified ),参考 超文本传输协议 的实体标记
If-Range 如果该实体未被修改过,则向返回所缺少的那一个或多个部分。否则,返回整个新的实体
If-Unmodified-Since 仅当该实体自某个特定时间以来未被修改的情况下,才发送回应。
Max-Forwards 限制该消息可被代理及网关转发的次数。
Origin 发起一个针对跨域资源共享的请求(该请求要求服务器在响应中加入一个Access-Control-Allow-Origin的消息头,表示访问控制所允许的来源)。
Pragma 与具体的实现相关,这些字段可能在请求/回应链中的任何时候产生。
Proxy-Authorization 用于向代理进行认证的认证信息。
Range 表示请求某个实体的一部分,字节偏移以0开始。
Referer 表示浏览器所访问的前一个页面,可以认为是之前访问页面的链接将浏览器带到了当前页面。Referer其实是Referrer这个单词,但RFC制作标准时给拼错了,后来也就将错就错使用Referer了。
TE 浏览器预期接受的传输时的编码方式:可使用回应协议头Transfer-Encoding中的值(还可以使用"trailers"表示数据传输时的分块方式)用来表示浏览器希望在最后一个大小为0的块之后还接收到一些额外的字段。
User-Agent 浏览器的身份标识字符串
Upgrade 要求服务器升级到一个高版本协议。
Via 告诉服务器,这个请求是由哪些代理发出的。
Warning 一个一般性的警告,表示在实体内容体中可能存在错误。
请求头 Content-Type

更全面:https://www.runoob.com/http/http-content-type.html

例子:

headers: {
	'Content-Type': 'multipart/form-data;charset=UTF-8'
}

charset—字符集

utf-8是全球通用的一种编码

常见的媒体格式类型如下:

text/html : HTML格式
text/plain :纯文本格式      
text/xml :  XML格式
image/gif :gif图片格式    
image/jpeg :jpg图片格式 
image/png:png图片格式

以application开头的媒体格式类型:

 application/xhtml+xml :XHTML格式
 application/xml     : XML数据格式
 application/atom+xml  :Atom XML聚合格式    
 application/json    : JSON数据格式
 application/pdf       :pdf格式  
 application/msword  : Word文档格式
 application/octet-stream : 二进制流数据(如常见的文件下载)
 application/x-www-form-urlencoded : <form encType=””>中默认的encType,form表单数据被编码为key/value格式发送到服务器(表单默认的提交数据的格式)
 

以audio开头的常见媒体格式文件:

'audio/x-wav' : wav文件
'audio/x-ms-wma' : wma 文件
'audio/mp3' : mp3文件

以video开头的常见媒体格式文件:

 'video/x-ms-wmv' : wmv文件
 'video/mpeg4' : mp4文件
 'video/avi' : avi文件

另外一种常见的媒体格式是上传文件之时使用的:

multipart/form-data : 需要在表单中进行文件上传时,就需要使用该格式
get请求没有Content-Type

表单数据会被encodeURIComponent后以参数的形式:name1=value1&name2=value2 附带在url?后面,再发送给服务器,并在url中显示出来。

**GET 请求不存在请求实体部分,键值对参数放置在 URL 尾部,**表单数据会被encodeURIComponent后以参数的形式:name1=value1&name2=value2 附带在url?后面,再发送给服务器,并在url中显示出来。因此请求头不需要设置 Content-Type 字段。

post请求的Content-Type
application/x-www-form-urlencoded

对表单数据进行编码,数据以键值对在http请求体重发送给服务器

提交的数据按照 key1=val1&key2=val2 的方式进行编码,key 和 val 都进行了 URL 编码。

得到类似这样的结果

POST http://www.example.com HTTP/1.1

Content-Type: application/x-www-form-urlencoded;charset=utf-8

title=test&sub%5B%5D=1&sub%5B%5D=2&sub%5B%5D=3  // 请求体,URL编码
multipart/form-data

可搭配 form表单FormData 对象使用。详情参考 JavaScript.md 文件的 FormData

以消息的形式发送给服务器:会将表单的每个数据处理为一条消息,用分隔符分开。

在表单中进行文件上传时,就需要使用该格式。

既可以上传键值对,也可以上传文件。

上传文件是以二进制(binary)的形式提交。

form表单

<form action="/upload" enctype="multipart/form-data" method="post">
    Username: <input type="text" name="username">
    Password: <input type="password" name="password">
    File: <input type="file" name="file">
    <input type="submit">
</form>

请求:

POST http://www.example.com HTTP/1.1

Content-Type:multipart/form-data; boundary=----WebKitFormBoundaryrGKCBY7qhFd3TrwA

 
------WebKitFormBoundaryrGKCBY7qhFd3TrwA
Content-Disposition: form-data; name="text"

title

------WebKitFormBoundaryrGKCBY7qhFd3TrwA
Content-Disposition: form-data; name="file"; filename="chrome.png"
Content-Type: image/png
 
PNG ... content of chrome.png ...

------WebKitFormBoundaryrGKCBY7qhFd3TrwA--

首先生成了一个 boundary 用于分割不同的字段,为了避免与正文内容重复,boundary 很长很复杂。然后 Content-Type 里指明了数据是以 multipart/form-data 来编码,本次请求的 boundary 是什么内容。消息主体里按照字段个数又分为多个结构类似的部分,每部分都是以 --boundary 开始,紧接着是内容描述信息,然后是回车,最后是字段具体内容(文本或二进制)。如果传输的是文件,还要包含文件名和文件类型信息。消息主体最后以 --boundary-- 标示结束。

application/json
axios.post('http://www.example.com HTTP/1.1',{
	title:"test",   
	sub:[1,2,3]
})
POST http://www.example.com HTTP/1.1 
Content-Type: application/json;charset=utf-8
// 请求体
{
	"title":"test",   
	"sub":[1,2,3]
}
text/xml
POST http://www.example.com HTTP/1.1 
Content-Type: text/xml
// 请求体
<?xml version="1.0"?>
<methodCall>
    <methodName>examples.getStateName</methodName>
    <params>
        <param>
            <value><i4>41</i4></value>
        </param>
    </params>
</methodCall>

HTTP请求方法

1 GET 请求指定的页面信息,并返回实体主体。
2 HEAD 类似于 GET 请求,只不过返回的响应中没有具体的内容,用于获取报头
3 POST 向指定资源提交数据进行处理请求(例如提交表单或者上传文件)。数据被包含在请求体中。POST 请求可能会导致新的资源的建立和/或已有资源的修改。
4 PUT 从客户端向服务器传送的数据取代指定的文档的内容。
5 DELETE 请求服务器删除指定的资源。
6 CONNECT HTTP/1.1 协议中预留给能够将连接改为管道方式的代理服务器。
7 OPTIONS 允许客户端查看服务器的性能。
8 TRACE 回显服务器收到的请求,主要用于测试或诊断。
9 PATCH 是对 PUT 方法的补充,用来对已知资源进行局部更新 。
  • GET 和 POST 的区别
  1. get参数通过url传递,post放在request body中。
  2. get请求在url中传递的参数是有长度限制的,而post理论上没有。
  3. get比post更不安全,因为参数直接暴露在url中,所以不能用来传递敏感信息。
  4. get请求只能进行url编码,而post支持多种编码方式。
  5. get请求浏览器会主动缓存。
  6. get请求参数会被完整保留在浏览历史记录里,而post中的参数不会被保留。

HTTP

超文本传输协议(英文:HyperText Transfer Protocol,缩写:HTTP),它是基于TCP协议的应用层传输协议,简单来说就是客户端和服务端进行数据传输的一种规则。

HTTP协议采用了请求/响应模型,浏览器作为HTTP客户端通过URL向HTTP服务端即WEB服务器发送所有请求。Web服务器根据接收到的请求后,向客户端发送响应信息。限制每次连接只处理一个请求。

HTTP协议是无状态协议,对于事务处理没有记忆能力。

HTTPS

HTTPS 协议(Hyper Text Transfer Protocol Secure):一般理解为HTTP+SSL/TLS,通过 SSL证书来验证服务器的身份,并为浏览器和服务器之间的通信进行加密。

http默认运行在80端口,https默认运行在443端口。

http的不足
  • 通信使用未加密的明文,内容容易被窃取
  • 不验证通信方的身份,容易遭遇伪装
  • 无法验证报文的完整性,容易被篡改

https就是为了解决上述http协议的安全性问题诞生的。https并非是应用层的新协议,是基于http协议的,在http与tcp通信之间新增SSL/TLS协议。

http: IP ➜ TCP ➜ HTTP(应用层)

https: IP ➜ TCP ➜ SSL / TLS ➜ HTTP(应用层)

SSL / TLS
  • SSL(Secure Sockets Layer安全套接字层协议)
  • TLS(Transport Layer Security传输层安全协议)
浏览器在使用HTTPS传输数据的流程

imgHTTPS数据传输流程

  1. 首先客户端通过URL访问服务器建立SSL连接。
  2. 服务端收到客户端请求后,会将网站支持的证书信息(证书中包含公钥)传送一份给客户端。
  3. 客户端的服务器开始协商SSL连接的安全等级,也就是信息加密的等级。
  4. 客户端的浏览器根据双方同意的安全等级,建立会话密钥,然后利用网站的公钥将会话密钥加密,并传送给网站。
  5. 服务器利用自己的私钥解密出会话密钥。
  6. 服务器利用会话密钥加密与客户端之间的通信。
HTTPS的不足

1、HTTPS协议多次握手,导致页面的加载时间延长近50%;

2、HTTPS连接缓存不如HTTP高效,会增加数据开销和功耗;

3、申请SSL证书需要钱,功能越强大的证书费用越高。

4、SSL涉及到的安全算法会消耗 CPU 资源,对服务器资源消耗较大。

5、不是绝对安全的,掌握加密算法的组织同样可以进行中间人形式的攻击

总结HTTPS和HTTP的区别

HTTPS是HTTP协议的安全版本,HTTP协议的数据传输是明文的,是不安全的,HTTPS使用了SSL/TLS协议进行了加密处理。

HTTP2

必看:https://mp.weixin.qq.com/s/YZaxf9GBvh_rT8hRx1XPbA

对于同一个域名,浏览器最多只能同时创建 6~8 个 TCP 连接 (不同浏览器不一样)。http2是基于http1进行了性能优化。http/1.x 是一个文本协议,而 http2 是一个彻彻底底的二进制协议。

HTTP 2.0 没有功能上的新增,只是优化了性能。

http2 本身的机制就足够快:

  1. http2采用二进制分帧的方式进行通信,而 http1.x 是用文本,http2 的效率更高

    流:流是连接中的一个虚拟信道,可以承载双向的消息;每个流都有一个唯一的整数标识符(1、2…N); 消息:是指逻辑上的 HTTP 消息,比如请求、响应等,由一或多个帧组成。 帧:HTTP 2.0 通信的最小单位,每个帧包含帧首部,至少也会标识出当前帧所属的流,承载着特定类型的数据,如 HTTP 头部等等

    http1.x 必须按照顺序传输。http2采用流和帧的方式,帧对数据进行顺序标识,多个帧之间可以并行发送,接收端根据帧首部的流标识可以重新组装。

  2. http2 可以进行多路复用,即跟同一个域名通信,仅需要一个 TCP 建立请求通道,请求与响应可以同时基于此通道进行双向通信,而 http1.x 每次请求需要建立 TCP,多次请求需要多次连接,还有并发限制,十分耗时

  3. http2 可以头部压缩,能够节省消息头占用的网络的流量,而HTTP/1.x每次请求,都会携带大量冗余头信息,浪费了很多带宽资源

  4. http2 可以进行服务端推送,我们平时解析 HTML 后碰到相关标签才会进而请求 css 和 js 资源,而 http2 可以直接将相关资源直接推送,无需请求,这大大减少了多次请求的耗时。

    这允许服务器直接提供浏览器渲染页面所需资源,而无需浏览器在收到、解析页面后再提起一轮请求,节约了加载时间。比如浏览器向服务器请求一个页面,之前需要等到浏览器收到页面解析html后,发现里面引用了静态资源,浏览器再向服务器发送静态资源的请求。但是现在服务器可以直接将页面和所需的静态资源一并返回。

    服务端推送需要开发人员手动配置,之前介绍的多路复用、头部header压缩这两项浏览器和服务器可以自行实现,不需要开发人员关心。

  5. chrome 和 firefox 都表示只会开发基于 https 的 http2,所以基本意味着使用 http2 的前提是必须是 https。升级HTTP2.0:如果你使用NGINX,只要在配置文件中启动相应的协议就可以了。

http2 在网络通畅+高性能设备下的表现没有比 http1.1有明显的优势,但是网络越差,设备越差的情况下 http2 对加载的影响是质的,可以说 http2 是为移动 web 而生的,反而在光纤加持的高性能PC 上优势不太明显

二进制分帧

img

img

多路复用

在 http/1.x 情况下,每个 http 请求都会建立一个 TCP 连接,这就意味着每个请求都需要进行三次握手。这样子就会浪费比较多的时间和资源,这点在 http/1.x 的情况下是没有办法避免的。并且浏览器会限制同一个域名下并发请求的个数。所以,在 http/1.x 的情况下,一个常见的优化手段是把静态资源分布到不同域名下,以此来突破浏览器并发数的限制。

http/1.x针对上述情况的优化手段:

浏览器一般会同一个域名建立 6-8 个 TCP 链接,也就是 6-8 个队。

如果把静态资源部署在不同的域名下,这样每个域名都能并发 6-8 个下载请求,网页打开的速度自然就会快很多。这种优化手段叫做“域名分片”,CDN 一般都支持这个。

在 http2 的情况下,所有的请求都会共用一个 TCP 连接,这个可以说是 http2 杀手级的特性了。 :punch: 因为这点,许多在 http/1.x 时代的优化手段都可以退休了。但是这里也出现了一个问题,所有的请求都共用一个 TCP 连接,那么客户端/服务端怎么知道某一帧(别忘记上面说了 http2 是的基本单位是帧)的数据属于哪个请求呢?

上面的 Stream Identifier 就是用来标识该帧属于哪个请求的。

当客户端同时向服务端发起多个请求,那么这些请求会被分解成一一个的帧,每个帧都会在一个 TCP 链路中无序的传输,同一个请求的帧的 Stream Identifier 都是一样的。当帧到达服务端之后,就可以根据 Stream Identifier 来重新组合得到完整的请求。

头部压缩

在 http/1.x 协议中,每次请求都会携带 header 数据,而类似 User-Agent, Accept-Language 等信息在每次请求过程中几乎是不变的,那么这些信息在每次请求过程中就变成了浪费。所以, http2 中提出了一个 HPACK 的压缩方式,用于减少 http header 在每次请求中消耗的流量。

具体做法是把报文头信息中常见的名和值对应一个索引,维护了一张静态字典,index从1到61,比如把:method:GET映射成2,这样就能达到压缩头部的作用。但是对于一些动态的资源,比如,user-agent,需要维护一份可动态添加内容的共同动态字典,这份动态字典在数据传输的过程中逐步建立,index从62开始。然后将映射之后的数据用huffman哈夫曼编码。

TCP

TCP是传输层的协议,是面向连接的协议,在收发数据前,必须和对方建立可靠的连接。

TCP报文格式

  1. TCP报头中的源端口号和目的端口号同IP数据报中的源IP与目的IP唯一确定一条TCP连接。TCP在发送数据前必须在彼此间建立连接,这里连接意思是:双方需要内保存对方信息(例如:IP,Port…)

  2. 报文主要段的意思

    • 序号seq:表示发送的数据字节流,确保TCP传输有序,对每个字节编号

    • 确认序号ack:发送方期待接收的下一序列号,接收成功后的数据字节序列号加 1。只有ACK=1时才有效。

    • ACK:确认序号的标志,ACK=1表示确认序号有效,ACK=0表示报文不含确认序号信息

    • SYN:连接请求序号标志,用于建立连接,SYN=1表示请求连接

    • FIN:结束标志,用于释放连接,为1表示关闭本方数据流

三次握手

位码即tcp标志位,有6种标示:

  • SYN(synchronous建立联机)
  • ACK(acknowledgement 确认)
  • PSH(push传送)
  • FIN(finish结束)
  • RST(reset重置)
  • URG(urgent紧急)

建立TCP连接时,需要客户端和服务器共发送3个包。

  • 第一次:客户端发送初始序号seq=x和SYN=1请求标志

  • 第二次:服务器发送请求标志SYN=1,发送确认标志ACK=1,发送自己的序号seq=y,发送客户端的确认序号ack=x+1

  • 第三次:客户端发送确认标志ACK=1,发送自己的序号seq=x+1,发送对方的确认号ack=y+1

过程分析:

  • 第一次:客户端发送请求到服务器,服务器知道客户端发送,自己接收正常。SYN=1,seq=x

  • 第二次:服务器发给客户端,客户端知道自己发送、接收正常,服务器接收、发送正常。ACK=1,ack=x+1,SYN=1,seq=y

  • 第三次:客户端发给服务器:服务器知道客户端接收正常,自己发送也正常。seq=x+1,ACK=1,ack=y+1

三次握手的原因:

上面分析过程可以看出,握手两次达不到让双方都得出自己、对方的接收、发送能力都正常的结论的。

四次挥手
  • 第一次挥手:客户端发出释放FIN=1,自己序列号seq=u,进入FIN-WAIT-1状态

  • 第二次挥手:服务器收到客户端的后,发出ACK=1确认标志和客户端的确认号ack=u+1,自己的序列号seq=v,进入CLOSE-WAIT状态

  • 第三次挥手:客户端收到服务器确认结果后,进入FIN-WAIT-2状态。此时服务器发送释放FIN=1信号,确认标志ACK=1,确认序号ack=u+1,自己序号seq=w,服务器进入LAST-ACK(最后确认态)

  • 第四次挥手:客户端收到回复后,发送确认ACK=1,ack=w+1,自己的seq=u+1,客户端进入TIME-WAIT(时间等待)。客户端经过2个最长报文段寿命后,客户端CLOSE;服务器收到确认后,立刻进入CLOSE状态。

过程分析:

  • 第一次:客户端请求断开FIN=1,seq=u

  • 第二次:服务器确认客户端的断开请求ACK,ack=u+1,seq=v

  • 第三次:服务器等数据传输完了,请求断开FIN=1,seq=w,ACK=1,ack=u+1

  • 第四次:客户端确认服务器的断开请求,ACK=1,ack=w+1,seq=u+1

四次挥手为何不是三次的原因是:

关闭连接时,当收到对方的FIN报文通知时,不代表本次TCP连接断开,它仅仅表示对方没有数据发送给你了,但未必你所有的数据都全部发送给对方了,因此你未必会马上会关闭连接。所以你在接收到对方的FIN并回复ACK之后,仍然能够传输数据,直到你自身数据传输完毕,自身再发送FIN,对端回复ACK后,才代表本次回话结束。

UDP

UDP是传输层的协议,是一种无连接的协议,传输数据之前源端和终端不建立连接、

TCP和UDP的区别

  • 1.UDP
    • 1.无连接
    • 2.面向报文,只是报文的搬运工
    • 3.不可靠,没有拥塞控制
    • 4.高效,头部开销只有8字节
    • 5.支持一对一、一对多、多对多、多对一
    • 6.适合直播、视频、语音、会议等实时性要求高的
  • 2.TCP
    • 1.面向连接:传输前需要先连接
    • 2.可靠的传输
    • 3.流量控制:发送方不会发送速度过快,超过接收方的处理能力
    • 4.拥塞控制:当网络负载过多时能限制发送方的发送速率
    • 5.不提供时延保障
    • 6.不提供最小带宽保障

TCP/IP 协议

TCP/IP协议是一个协议簇。里面包括很多协议的,之所以命名为TCP/IP协议,因为TCP、IP协议是两个很重要的协议,就用他两命名了。

1、TCP/IP模型是一系列网络协议的总称,这些协议的目的是使得计算机之间可以进行信息交换,

2、TCP/IP模型四层架构从下到上分别是链路层,网络层,传输层,应用层

3、链路层的作用是负责建立电路连接,是整个网络的物理基础,典型的协议包括以太网,ADSL等,

4、网络层负责分配地址和传送二进制数据,主要协议是IP协议,

5、传输层负责传送文本数据,主要协议是TCP

6、应用层负责传送各种最终形态的数据,是直接与用户信息打交道的层,主要协议是http,ftp等

DNS

网络通讯大部分是基于TCP/IP的,而TCP/IP是基于IP地址的,所以计算机在网络上进行通讯时只能识别如“202.96.134.133”之类的IP地址,而不能认识域名。

DNS( Domain Name System)是“域名系统”的英文缩写,它用于TCP/IP网络,它所提供的服务是用来将主机名和域名转换为IP地址的工作。

DNS 的过程

DNS是应用层协议,事实上他是为其他应用层协议工作的,包括不限于HTTP和SMTP以及FTP,用于将用户提供的主机名解析为ip地址。 具体过程如下: 1、用户主机上运行着DNS的客户端,就是我们的PC机或者手机客户端运行着DNS客户端了

2、浏览器将接收到的url中抽取出域名字段,就是访问的主机名,比如 http://www.baidu.com/ ,并将这个主机名传送给DNS应用的客户端

3、DNS客户机端向DNS服务器端发送一份查询报文,报文中包含着要访问的主机名字段(中间包括一些列缓存查询以及分布式DNS集群的工作)

4、该DNS客户机最终会收到一份回答报文,其中包含有该主机名对应的IP地址

5、一旦该浏览器收到来自DNS的IP地址,就可以向该IP地址定位的HTTP服务器发起TCP连接

DNS 解析过程

在解析过程中,按照:

  • 浏览器缓存
  • 系统缓存
  • 路由器缓存
  • ISP(运营商)DNS缓存
  • 根域名服务器
  • 顶级域名服务器
  • 主域名服务器

的顺序逐步读取缓存,直到拿到IP地址。

1、 浏览器缓存

  当用户通过浏览器访问某域名时,浏览器首先会在自己的缓存中查找是否有该域名对应的IP地址(若曾经访问过该域名且没有清空缓存便存在);

2、系统缓存

  当浏览器缓存中无域名对应IP则会自动检查用户计算机系统Hosts文件DNS缓存是否有该域名对应IP;

3、 路由器缓存

  当浏览器及系统缓存中均无域名对应IP则进入路由器缓存中检查,以上三步均为客服端的DNS缓存;

4、 ISP(互联网服务提供商)DNS缓存

  当在用户客服端查找不到域名对应IP地址,则将进入ISP DNS缓存中进行查询。比如你用的是电信的网络,则会进入电信的DNS缓存服务器中进行查找;

5、根域名服务器

  当以上均未完成,则进入根服务器进行查询。全球仅有13台根域名服务器,1个主根域名服务器,其余12为辅根域名服务器。根域名收到请求后会查看区域文件记录,若无则将其管辖范围内顶级域名(如.com)服务器IP告诉本地DNS服务器;

6、顶级域名服务器

  顶级域名服务器收到请求后查看区域文件记录,若无则将其管辖范围内主域名服务器的IP地址告诉本地DNS服务器;

7、主域名服务器

  主域名服务器接受到请求后查询自己的缓存,如果没有则进入下一级域名服务器进行查找,并重复该步骤直至找到正确纪录;

8、保存结果至缓存

  本地域名服务器把返回的结果保存到缓存,以备下一次使用,同时将该结果反馈给客户端,客户端通过这个IP地址与web服务器建立链接。

DNS 预解析

优化DNS,我们可以考虑两个方向:

  1. 减少DNS请求次数
  2. 缩短DNS解析时间dns-prefetch

现代浏览器为了优化DNS解析,也设有了浏览器DNS缓存。每当在首次DNS解析后会对其IP进行缓存。至于缓存时长,每种浏览器都不一样。

DNS 实现域名到IP的映射。通过域名访问站点,每次请求都要做DNS解析。每次DNS解析,通常在200ms以下。针对DNS解析耗时问题,一些浏览器通过DNS Prefetch 来提高访问的流畅性。

DNS Prefetch 是一种DNS 预解析技术,当浏览网页时,浏览器会在加载网页时对网页中的域名进行解析缓存,这样在单击当前网页中的连接时就无需进行DNS的解析,减少用户等待时间,提高用户体验。

DNS 预解析是一项让浏览器主动去执行域名解析的功能。

浏览器试图在用户访问链接之前解析域名,其范围包括文档的所有链接,无论是图片的,CSS 的,还是 JavaScript 等其他用户能够点击的 URL。

  • HTTP 协议

会自动预解析<img>(外域图片)、<script>(外域JS代码)、<link>(外域CSS代码、HTML代码)、<a>(外域链接)

如需关闭:

<meta http-equiv="x-dns-prefetch-control" content="off">
  • HTTPS 协议

不会自动预解析:处于安全考虑——防止窃听者根据 DNS 预解析推断显示在HTTPS页面中超链接的主机名

如需开启:

<meta http-equiv="x-dns-prefetch-control" content="off">

如果要控制浏览器端是否对域名进行预解析,可以通过Http header 的x-dns-prefetch-control 属性进行控制。

<meta http-equiv="x-dns-prefetch-control" content="on" />   // 允许域名预解析

<meta http-equiv="x-dns-prefetch-control" content="off" />   // 不允许域名预解析

https协议下,允许预解析多个特定链接的做法

dns-prefetch 仅对跨域域上的 DNS查找有效,因此请避免使用它来指向相同域

<link rel="dns-prefetch" href="http://bdimg.share.baidu.com" />
<link rel="dns-prefetch" href="http://nsclick.baidu.com" />
<link rel="dns-prefetch" href="http://hm.baidu.com" />

这样在单击当前网页中的连接时就无需进行DNS的解析,减少用户等待时间,提高用户体验。

迭代和递归

通常情况下,主机向本地DNS服务器的查询一般都是采用递归查询,而本地名称服务器向 DNS(一级、二级、三级...)名称服务器采用迭代查询

主机的 DNS 查询主要有两种方式,递归查询 和 迭代查询,其主要区别为:

请求头RD:表示是否期望使用递归查询,0—迭代,1—递归(默认)

响应头RA:表示是否支持递归查询,0—不支持,1—支持

递归查询

DNS的解析过程默认是采用递归查询

递归查询是通过递归的访问各级域名服务器,查询到ip地址后递归的返回给客户端,流程如下:

1.客户端向本地DNS服务器发起请求查询www.xxxx.com的ip地址

2.本地DNS服务器向根DNS服务器发起解析请求

3.根DNS服务器收到请求后,向.com顶级DNS服务器发起查询请求

4..com顶级DNS服务器收到请求后,向.xxxx.com权威服务器发起查询请求

5..xxxx.com返回www.xxxx.com所对应的ip地址给.com顶级DNS服务器

6.顶级DNS服务器返回ip地址给根DNS服务器

7.根DNS服务器返回ip给本地DNS服务器

8.本地DNS服务器把ip进行缓存,并返回给客户端

递归查询

迭代查询

首先客户端向本地域名服务器发起请求,如果本地域名服务器没有缓存记录,客户端便会依次对根域名服务器、顶级域名服务器和二级域名服务器等发起迭代查询,直到获得最终的查询结果。

在以下条件之一满足时,就会采用迭代解析方式:

1.在查询本地域名服务器时,如果客户端的请求报文中申请使用迭代查询,即在DNS请求报文中的RD字段设置为0。

2.客户端在DNS请求报文中申请使用递归查询,但所配置的本地域名服务器禁止使用递归查询,即在应答DNS报文头部的RA字段设置为0。

img

从理论上讲,任何 DNS 查询既可以是递归的,也可以是迭代的

递归DNS有什么优势?

由于缓存递归DNS查询通常比迭代查询的解析速度更快。递归DNS服务器将对执行的每个查询的最终答案进行缓存,并将该最终答案保存一定的时间(称为生存时间)。

当递归解析器收到对其缓存中已经具有的IP地址的查询时,它可以快速将缓存的答案提供给客户端,而无需与任何其他DNS服务器进行通信。

递归DNS的缺点是什么?

不幸的是,在开放的DNS服务器上允许递归DNS查询会造成安全漏洞,因为此配置可使攻击者执行DNS放大攻击和DNS缓存中毒,而且递归查询过程中需要服务器消耗更多的内存来保存状态,增大服务器压力。

因此客户端向本地DNS服务器查询采用递归,本地DNS服务器向其他DNS服务器查询采用迭代,如图所示:

img

拓展
预加载 rel="preload"

增加请求优先级

<link rel="preload" href="test.css"> 
预连接 rel="preconnect"

与 DNS 预解析类似,Preconnect 不仅完成 DNS 预解析,同时还将进行 TCP 握手和建立传输层协议

<link rel="preconnect" href="http://example.com">
预获取 rel="prefetch"

如果我们确定某个资源(图片/脚本文件/css文件)将来一定会被使用到,我们可以让浏览器预先请求该资源并放入浏览器缓存中。与 DNS 预解析不同,预获取真正请求并下载了资源,并储存在缓存

<link rel="prefetch" href="test.css">
预渲染 rel="prerender"

对一个用户将来一定会打开的 tab 页:将【下载所有资源、创建 DOM 结构、完成页面布局、应用 CSS 样式和执行 JavaScript 脚本】等。当用户真正访问该链接时,隐藏的页面就切换为可见,使页面看起来就是瞬间加载完成一样

<link rel="prerender" href="http://example.com">

前提是:用户在将来一定会打开该tab页,否则会造成不必要的资源浪费

TCP/IP 四层模型

应用层:各种应用程序和网络之间的接口,其功能是直接向用户提供服务,常用协议HTTP,SMTP,FTP

传输层:建立/管理端对端的接口,TCP,UDP

网络层:IP选址,为数据包选择路由,IP,ICMP

数据链路层:传输有地址的帧

TCP/IP 五层模型

应用层:各种应用程序和网络之间的接口,其功能是直接向用户提供服务,常用协议HTTP,SMTP,FTP

传输层:建立/管理端对端的接口,TCP,UDP

网络层:IP选址,为数据包选择路由,IP,ICMP

数据链路层:传输有地址的帧

物理层:二进制的数据形式在物理媒体上传输数据

OSI七层模型

应用层:各种应用程序和网络之间的接口,其功能是直接向用户提供服务常用协议HTTP,SMTP,FTP

表示层:数据格式化,代码转换,数据加密

会话层:建立/管理/解除会话

传输层:建立/管理端对端的接口,TCP,UDP

网络层:IP选址,为数据包选择路由,IP,ICMP

数据链路层:传输有地址的帧

物理层:二进制的数据形式在物理媒体上传输数据,例如电缆、光纤、网卡、集线器等等

前端安全

攻击方式
  • XSS 攻击
  • CSRF 攻击
  • 点击劫持
  • 中间人攻击
XSS 攻击

https://www.cnblogs.com/tugenhua0707/p/10909284.html#_labe1

XSS即Cross Site Scripting(跨站脚本攻击),指的是攻击者想尽一切办法将一些可执行的代码注入到网页中,利用这些恶意脚本,攻击者可获取用户的敏感信息如 Cookie、SessionID 等,进而危害数据安全。为了不和层叠样式表CSS混淆,故将其缩写为 XSS。

XSS 可以分为:存储型 XSS (也叫持久型 XSS)、反射型 XSS (也叫非持久型)。

存储型

存储型也就是攻击的代码被服务端写入进数据库中,这种攻击危害性很大,因为如果网站访问量很大的话,就会导致大量正常访问页面的用户都受到攻击。

这种攻击常见于带有用户保存数据的网站功能,如论坛发帖、商品评论、用户私信等。具有攻击性的脚本被保存到了服务器并且可以被普通用户完整的从服务的取得并执行,从而获得了在网络上传播的能力。

存储型XSS的攻击步骤如下:

  1. 攻击者将恶意代码提交到目标网站数据库中。
  2. 用户打开目标网站时,网站服务器将恶意代码从数据库中取出,然后拼接到html中返回给浏览器中。
  3. 用户浏览器接收到响应后解析执行,那么其中的恶意代码也会被执行。
  4. 那么恶意代码执行后,就能获取到用户数据,比如上面的cookie等信息,那么把该cookie发送到攻击者网站中,那么攻击者拿到该cookie然后会冒充该用户的行为,调用目标网站接口等违法操作。

例子

当我们在做评论时,用户输入的内容未经过过滤直接保存到数据库中。

攻击者可以构建一条评论, 包含恶意内容:

质量非常不错!<script src="danger.com/spread.js"></script>

当你的评论列表被用户浏览时, 直接从服务端取出,回填到HTML响应中:

<li>质量非常不错!<script src="danger.com/spread.js"></script></li>

那么浏览器会加载执行恶意脚本danger.com/spread.js, 在恶意脚本中利用用户的登录状态发更多的带有恶意评论的URL, 诱导更多人点击,层层传播,放大攻击范围。

反射型

反射型也叫非持久型,相比于前者危害就小一些,一般通过修改 URL 参数的方式加入攻击代码,诱导用户访问链接从而进行攻击。

这种常见于通过 URL 传递参数的功能,如网站搜索、跳转等。由于需要用户主动打开恶意的 URL 才能生效,攻击者往往会结合多种手段诱导用户点击。

反射型XSS的攻击步骤如下:

  1. 攻击者在url后面的参数中加入恶意攻击代码。
  2. 当用户打开带有恶意代码的URL的时候,网站服务端将恶意代码从URL中取出,拼接在html中并且返回给浏览器端。
  3. 用户浏览器接收到响应后执行解析,其中的恶意代码也会被执行到。
  4. 攻击者通过恶意代码来窃取到用户数据并发送到攻击者的网站。攻击者会获取到比如cookie等信息,然后使用该信息来冒充合法用户 的行为,调用目标网站接口执行攻击等操作。

例子

比如:攻击者通过电子邮件等方式将包含注入脚本的恶意链接发送给受害者,当受害者点击该链接的时候,注入脚本被传输到目标服务器上,然后服务器将注入脚本 "反射"到受害者的浏览器上,从而浏览器就执行了该脚本。

恶意链接形式:

http://localhost:8080/helloController/search?name=<script src="danger.com/spread.js"></script>

对于这种攻击方式来说,如果用户使用的是Chrome 浏览器的话,浏览器已经帮助用户做了防御攻击。但是我们也不能说就不防御了,因为无法保证用户都是用有防御攻击的浏览器。

二者区别:存储型 XSS 的恶意代码存在数据库里,反射型 XSS 的恶意代码存在 URL 里。

防御

1.输入输出过滤

一切用户输入皆不可信,在输出时进行验证,一般做法是将 ‘ ” < > & 这些个危险字符进行转义。

对于URL地址的转义可以使用encodeURI,当你需要编码URL中的参数的时候,那么encodeURIComponent是最好方法。

转义的方式很明显并不适用于所有场景,比如富文本,这样会将需要的格式都过滤掉。因为HTML标签种类繁多,基于黑名单的过滤方法考虑的并不全面,所以我们可以根据白名单过滤HTML, 可以借助xss.js来完成。

后端需要对提交的数据进行过滤。

2. Cookie 的 HttpOnly

很多XSS攻击目标都是窃取用户cookie伪造身份认证。

设置了 HttpOnly 属性的 cookie 不能使用 JavaScript 经由 Document.cookie 属性、XMLHttpRequest 和 Request APIs 进行访问,以防范跨站脚本攻击(XSS)。

可以通过服务端在 cookie 中设置 HttpOnly 属性,js脚本将无法读取到 cookie 信息。

ctx.cookies.set(name, value, {
    httpOnly: true // 默认为 true
})

3. 第三方库 DOMPurify (XSS过滤器)

4. CSP(内容安全策略)

CSP (Content Security Policy,内容安全策略)是 W3C 提出的 ,本质上就是白名单制度,开发者明确告诉浏览器哪些外部资源可以加载和执行。它的实现和执行全部由浏览器完成,我们只需提供配置。

CSP 大大增强了网页的安全性。攻击者即使发现了漏洞,也没法注入脚本,除非还控制了一台列入了白名单的可信主机。

两种方法可以启用 CSP:

  • 一种是通过 HTTP 头信息的Content-Security-Policy的字段
  • 另一种是通过网页的<meta>标签

方式1举例

Content-Security-Policy: default-src ‘self’

表示只允许加载本站资源

Content-Security-Policy: default-src https://demo.example.cn https://demo.example2.cn; object-src 'none'

CSP 的值中,不同属性以 ; 隔开,同一属性的多个值以空格隔开。上面例子的意思就是默认允许读取 https://demo.example.cnhttps://cdn.example2.net 的资源,object-src 使用的相关资源无白名单,也就是完全不允许读出。

如果使用了不符合要求的资源,浏览器会给予拦截,给出下面提示:

Refused to execute inline script because it violates the following Content Security Policy directive

我们也可以使用 meta 标签代替 HTTP 头:

<meta
  http-equiv="Content-Security-Policy"
  content="default-src https://cdn.example.net; child-src 'none'; object-src 'none'"
/>

Content-Security-Policy 的常用选项有这些:

  • default-src: 是 src 选项的默认值,但不能覆盖以下值:base-uriform-actionframe-ancestorsplugin-typesreport-urisandbox
  • base-uri:特别说一下<base> 标签是因为孤陋寡闻的我第一次见到。指定用于一个文档中包含的所有相对 URL 的根 URL,一个文件只能有一个 <base> 标签,用起来大概是这样的:<base target="_top" href="http://www.example.com/">
  • connect-src: XHR、WebSockets 等连接使用的地址
  • font-src:字体文件来源
  • img-src:图片地址
  • media-src:音视频地址
  • object-src:Flash 相关
  • report-uri:出现报错时提交到指定 uri,不能在 标签使用
  • style-src:样式文件
CSRF 攻击

跨站点请求伪造(Cross-Site Request Forgeries),也被称为 one-click attack 或者 session riding。冒充用户发起请求(在用户不知情的情况下), 完成一些违背用户意愿的事情(如修改用户信息,删除评论等)。

例子

银行网站A,它以GET请求来完成银行转账的操作,如:

http://www.mybank.com/Transfer.php?toBankId=11&money=1000

危险网站B,它里面有一段HTML的代码如下:

<img src=http://www.mybank.com/Transfer.php?toBankId=11&money=1000 />

首先,你登录了银行网站A,然后访问危险网站B,噢,这时你会发现你的银行账户少了1000块......

为什么会这样呢?原因是银行网站A违反了HTTP规范,使用GET请求更新资源。在访问危险网站B的之前,你已经登录了银行网站A,而B中的以GET的方式请求第三方资源(这里的第三方就是指银行网站了,原本这是一个合法的请求,但这里被不法分子利用了),所以你的浏览器会带上你的银行网站A的Cookie发出Get请求,去获取资源 http://www.mybank.com/Transfer.php?toBankId=11&money=1000,结果银行网站服务器收到请求后,认为这是一个更新资源操作(转账操作),所以就立刻进行转账操作......

例子虽然是get,post请求提交表单同样会被攻击。

CSRF攻击是有条件的

1、客户端必须一个网站并生成cookie凭证存储在浏览器中

2、该cookie没有清除,客户端又打开一个危险页面向受信任网站发送合法请求

CSRF攻击的特点
  • 通常发生在第三方网站
  • 攻击者不能获取cookie等信息,只是使用
防御
  • 验证码:强制用户必须与应用进行交互,才能完成最终请求。此种方式能很好的遏制 CSRF,但是用户体验相对差。
  • token:token 验证的 CSRF 防御机制是公认最合适的方案。
  • 尽量使用 post ,限制 get 使用;上一个例子可见,get 太容易被拿来做 CSRF 攻击,但是 post 也并不是万无一失,攻击者只需要构造一个form就可以。
  • Referer check:请求来源限制,此种方法成本最低,但是并不能保证 100% 有效,因为服务器并不是什么时候都能取到 Referer,而且低版本的浏览器存在伪造 Referer 的风险。
点击劫持

点击劫持(click hijacking)也称为 UI 覆盖攻击。

大概有两种方式:

1、攻击者使用一个透明的iframe,覆盖在一个网页上,然后诱使用户在该页面上进行操作,此时用户将在不知情的情况下点击透明的iframe页面;

2、攻击者使用一张图片覆盖在网页,遮挡网页原有位置的含义;

防御

点击劫持攻击需要首先将目标网站载入到恶意网站中,使用 iframe 载入网页是最有效的方法。所以可以设置我们的网页不允许使用iframe被加载到其他网页中就可以避免这种情况了。

响应头中设置X-Frame-Options(服务器端进行),X-Frame-Options可以设置以下三个值:

  1. DEBY:不允许任何网页使用iframe加载我这个页面。
  2. SAMEORIGIN:只允许在相同域名(也就是自己的网站)下使用iframe加载这个页面。
  3. ALLOWED-FROM origin: 允许任何网页通过iframe加载我这个网页。

这种方式在一些老旧的浏览器上是不支持的,具体可以通过can i use去查看

中间人攻击

中间人(Man-in-the-middle attack, MITM)是指攻击者和通讯的两端分别创建独立的联系,并交换其得到的数据,攻击者可以拦截通信双方的通话并插入新的内容。

图片

一般的过程如下:

  • 客户端发送请求到服务端,请求被中间人截获
  • 服务器向客户端发送公钥
  • 中间人截获公钥,保留在自己手上,然后自己生成一个【伪造的】公钥,发给客户端
  • 客户端收到伪造的公钥后,生成加密hash值发给服务器
  • 中间人获得加密hash值,用自己的私钥解密获得真秘钥,同时生成假的加密hash值,发给服务器
  • 服务器用私钥解密获得假密钥,然后加密数据传输给客户端
防御

采用HTTPS通信可以防御中间人攻击, 但是使用HTTPS并不就绝对安全,一方面你要完全关闭HTTP通信,如果没有完全关闭,攻击者可以通过某些方式将HTTPS 降级为HTTP,从而实现中间人攻击。

其次使用HTTPS通信,开发时也不要忽视证书的校验,或者对于非法证书不进行处理,这样也容易被中间人攻击。

SQL注入

通过把SQL命令插入到Web表单提交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令。 那通俗一点呢?这么来说吧,一般我们提交的表单数据(未经过滤的情况下)都会拼接到 SQL 查询语句中的,就例如:

SELECT * FROM users WHERE name='乔治大叔'

其中 name 的参数 乔治大叔 就是从表单中传过来的数据,如果传的参数是一条 SQL 语句,那么就可能骗过了 SQL 数据库,从而执行了一段恶意的代码。达到了我们(程序员)意料之外的结果。

防范

1、严格限制数据库权限

2、转义危险字符

3、过滤参数中含有的一些数据库关键词

文件上传漏洞

攻击者上传了一个可执行文件到服务器执行。

防范

1、前端:input 上传 限制 MIME类型,检查后缀名

2、后端:判断文件类型,结合MIME、后缀检查、Content-Type等方式。配合白名单方式,可以使用 file-type 这个库判断文件类型。

3、后端:检查上传路径,使用随机数改写文件名和文件路径

1
https://gitee.com/Jay_Ohhh/front-end-notes.git
git@gitee.com:Jay_Ohhh/front-end-notes.git
Jay_Ohhh
front-end-notes
front-end notes
master

搜索帮助