网络基础是一劳永逸的东西。
(必读:https://juejin.im/post/5e527c58e51d4526c654bf41)
什么是TCP/IP协议?什么是HTTP协议?
TCP/IP协议分为四层:应用层(DNS 域名系统,HTTP协议,FTP 文件传输协议)、传输层(TCP 传输控制协议,UDP用户数据报协议)、网络层(IP协议 一条传输路线)、链路层(处理连接网络的硬件部分)
发送端=》每通过一层,就增加首部
应用层: http数据
传输层:tcp首部+http数据
网络层:ip首部+tcp首部+ http数据
链路层:以太网首部+ip首部+tcp首部+ http数据
接收端=》每通过一层,就删除首部
链路层:以太网首部+ip首部+tcp首部+ http数据
网络层:ip首部+tcp首部+ http数据
传输层:tcp首部+http数据
应用层: http数据
IP协议的作用:将各个数据包准确无误地传递给对方
IP协议负责找到接收方的详细地址,TCP协议负责安全地把东西带给接收方。
TCP是传输层,通过字节流服务。将一大块数据分割成以报文段为单位的数据包,将数据安全准确可靠传送给对方。TCP为了更容易传送大数据才进行数据分割,TCP协议可以确认数据最终是否传送给对方。
为了保证信息准确无误可靠地送达,TCP采取了最著名的三次握手策略。
模拟:
A: 在吗? SYN
B:在的。啥事? SYN+ACK
A:去吃饭啦。ACK
转自:https://www.cnblogs.com/laojiao/p/9653108.html
HTTP协议特点:客户端每次发送请求都需要服务器回送响应,在请求结束后,会主动释放连接。从建立连接到关闭连接的过程叫做一次连接。
HTTP协议常见的状态码有哪些?
1--信息性状态码
2:200 ok 204 (请求成功,无资源返回) 206(对资源某一部分请求)--成功状态码
3: 301 308(永久重定向) 302,303,307(临时性重定向) 304(资源找到 不符合要求) 303(资源url已更新,是否能临时按照新的url访问)--重定向状态码
1)301 308是永久重定向,302,303,307是临时重定向,304是资源找到,不符合要求
2)301,302是http1.0的内容,303,307,308是http1.1的内容
3)301,302本来在规范中是不允许重定向时改变请求method的(将post改为get),实际上许多浏览器实现的时候,允许重定向改变请求method
4)303的出现是允许重定向时改变请求method。此外303响应禁止被缓存。307,308则不允许重定向时改变method。
4:400(报文错误)401(没有认证信息) 403(forbidden)404(not found) 405( 方法不被允许) 408(请求超时)--客户端错误状态码
5:500(服务器内部错误)502(网关错误)504(超时)503(超负荷)--服务器错误状态码
HTTP协议和HTTPS的区别?HTTP2了解吗?
- https需要使用ca证书,费用不便宜
- http是明文传输,https是通过ssl加密传输
- http连接简单,无状态(对事务处理没有记忆能力,服务器不知道客户端是什么状态)
- http的端口是80,https的端口是443
- https是通过ssl+http构建的用来进行身份认证、加密通信的网络协议,比使用http安全
HTTP2(关键并不在于高带宽,而是低延迟)
- 多路复用
- 首部压缩
- 支持服务器推送
- 二进制分帧
什么是三次握手?什么是四次挥手?
tcp连接
第一次ヽ(○´∀)乂(*´∀`*): client发送SYN=1给server(在?)
第二次ヽ(○´∀)乂(*´∀`*): server使用ACK应答,使用SYN同步,发送SYN+ACK给client(收到了,啥事?)
第三次ヽ(○´∀)乂(*´∀`*):client发送ACK应答(我中彩票了。)
tcp断开连接
第一次(ㄏ ̄▽ ̄)ㄏbyeㄟ( ̄▽ ̄ㄟ):client发起中断连接请求,发送Fin报文给server(我发Fin,想要断开连接)
第二次(ㄏ ̄▽ ̄)ㄏbyeㄟ( ̄▽ ̄ㄟ):server发送ACK应答(你发的Fin我收到了)
第三次(ㄏ ̄▽ ̄)ㄏbyeㄟ( ̄▽ ̄ㄟ):server发送Fin报文(我也发Fin,跟你断开连接)
第四次(ㄏ ̄▽ ̄)ㄏbyeㄟ( ̄▽ ̄ㄟ):client发送ACK应答(你发的Fin我也收到了)
Server只有等所有应答都发送完,才能发Fin报文。不能同时发送。
HTTP和websocket的区别?
答案一:
HTTP1.1通过使用Connection:keep-alive进行长连接,HTTP 1.1默认进行持久连接。在一次 TCP 连接中可以完成多个 HTTP 请求,但是对每个请求仍然要单独发 header,Keep-Alive不会永久保持连接,它有一个保持时间,可以在不同的服务器软件(如Apache)中设定这个时间。这种长连接是一种“伪链接”
websocket的长连接,是一个真的全双工。长连接第一次tcp链路建立之后,后续数据可以双方都进行发送,不需要发送请求头。
keep-alive双方并没有建立正真的连接会话,服务端可以在任何一次请求完成后关闭。WebSocket 它本身就规定了是正真的、双工的长连接,两边都必须要维持住连接的状态。
答案二:
(DNS FTP HTTP)应用-》(TCP)传输-》(IP)网络-》(硬件)链路
http协议是用在应用层的协议,它是基于tcp协议的,http协议建立链接必须要有三次握手才能发送信息。
http连接分为短连接、长连接。短连接是每次请求都要三次握手才能发送自己的信息,即每一个request对应一个response。长连接是在一定得期限内保持连接,保持tcp连接不断开。客户端与服务器通信,必须要有客户端发起然后服务器返回结果。客户端是主动的,服务器是被动的。
websocket是为了解决客户端发起多个http请求到服务器资源,浏览器必须经过长时间的轮询问题而生的。他实现了多路复用,它是全双工通信。在websocket协议下客户端和服务器可以同时发送信息。
建立websocket之后,服务器不必在浏览器发送request请求之后才能发送信息到浏览器。这是的服务器已有主动权想什么时候发就可以发送信息到服务器。而且信息当中不必在带有head的部分信息了。
与http的长连接通信来说,这种方式不久降低了服务器的压力,而且信息当中也减少了部分多余的信息。
什么是DNS?
DNS是建立在分布式数据库上的分层命名系统。
作用:将域名转换成ip地址,并可以将域名分配给internet组资源和用户,无论实体的物理位置如何。
请描述用户在浏览器里输入URL后回车,到页面完全展示期间,都发生了什么?
(DNS 解析过程,HTML词法分析和语法分析,CSS解析, 合成图层、合成线程调用光栅化线程池,生成位图后浏览器进程间通信过程,显卡缓存与显示器的关系)
- DNS解析:将域名解析成IP地址
- TCP连接:TCP三次握手
- 发送HTTP请求
- 服务器处理请求并返回HTTP报文
- 浏览器解析渲染页面
- 断开连接:TCP四次挥手
- URL:统一资源定位符,网址。
scheme://host.domain:port/path/filename
scheme-因特网服务的类型。常见协议有:http,https,ftp,file。最常见的是http,而https是进行加密的网络传输。
host-域主机(http的默认主机是www)
domain-因特网域名,比如w3school.com.cn
port-主机上的端口号(http默认是80端口,https默认是443端口)
path-服务器上的路径
filename-文档/资源的名称
---缓存
- 域名解析(DNS)
用于将主机名和域名转换成IP地址
解析过程:1、先从本地host文件中查找是否有对应域名的IP关系,如果有即向IP地址发起请求,如果没有,将到DNS服务器中查找。
2、DNS分为本地DNS服务器,根DNS服务器和各个子DNS服务器。
根DNS下面有com DNS服务器、org DNS服务器、edu DNS服务器 ,而上述三个服务器下马有yahoo.com、amazon.com、pbs.org、poly.edu等DNS服务器
DNS服务器递归查询和迭代查询
从浏览器到本地DNS服务器属于递归查询,而DNS服务器之间属于迭代查询。
- 建立TCP连接
三次握手
1、 client ----{发送SYN报文}syn=1 seq=x(发送报文序号为X)-->server === 客户端进入SYN_SEND状态,等待服务器确认
2、server ---{发送syn+ack报文}syn=1 ack=x+1(确认序号为X+1)seq=y(发送报文序号)-->client === 服务器进入SYN_RECV转态
3、client ---{发送报文ack}ack=y+1(确认序号为Y+1) seq=z(发送报文序号为Z)-->server === 客户端和服务器都进入ESTABLISHED状态,完成TCP三次握手。
为啥是三次握手而不是两次,四次呢?
1、为了防止已失效的连接请求报文段突然又传送到了服务端,因而产生错误
2、为了解决“网络中存在延迟的重复分组”的问题
“已失效的连接请求报文段”的产生在这样一种情况下:client发出的第一个连接请求报文段并没有丢失,而是在某个网络结点长时间的滞留了,以致延误到连接释放以后的某个时间才到达server。本来这是一个早已失效的报文段。但server收到此失效的连接请求报文段后,就误认为是client再次发出的一个新的连接请求。于是就向client发出确认报文段,同意建立连接。假设不采用“三次握手”,那么只要server发出确认,新的连接就建立了。由于现在client并没有发出建立连接的请求,因此不会理睬server的确认,也不会向server发送数据。但server却以为新的运输连接已经建立,并一直等待client发来数据。这样,server的很多资源就白白浪费掉了。采用“三次握手”的办法可以防止上述现象发生。例如刚才那种情况,client不会向server的确认发出确认。server由于收不到确认,就知道client并没有要求建立连接。”
谢希仁著《计算机网络》
- 浏览器发送HTTP请求报文,服务器返回HTTP响应报文
http报文包括:请求报头(请求方法、目标地址、遵循的协议)、请求主体
注意:浏览器只能发送get、post的方法,而打开网页使用的是get方法
请求网址:http://www.baidu.com/
请求方法:GET
远程地址:IP
状态码:200 OK
Http版本: HTTP/1.1
请求头:。。。
响应头:。。。
注意:响应头有一个Set-Cookie:"PHPSESSID=c882giens9f7d3oglcakhrl994; path=/",说明浏览器没有关于这个网站的cookie信息.而第二次发请求的时候,请求头包含了第一次响应的cookie信息。
Cookie用来保存一些有用的信息:1、如果首次访问,会提示服务器建立用户缓存信息2、如果不是,则利用cookies对应的键值,找到相应的缓存,缓存里面存放着用户名、密码和一些用户信息。
通过get请求,和服务器响应,可以将服务器上的目标文件传输到浏览器进行渲染。 - 渲染页面
客户端拿到服务器端传输过来的文件,首先查看Response header,根据不同的状态码,做不同的事。如果响应资源进行了压缩(比如gzip),还需要进行解压。然后对响应资源做缓存。接下来,找到HTML和MIME文件,通过MIME文件,浏览器知道用页面渲染引擎来处理HTML文件。
a、浏览器会解析html源码,然后创建一个DOM树。
在DOM树中,每一个HTML标签都有一个对应的节点,并且每一个文本都有一个对应的文本节点。
b、浏览器解析css代码,计算出最终的样式数据(将任何尺寸值到减少三个可能之一:auto px 百分比),形成CSS对象模型CSSOM
忽略非法样式。 按照浏览器默认设置--用户设置--外链样式--内联样式--html中的style样式顺序进行渲
c、利用dom和cssom构建一棵渲染树(rendering tree)
rendering tree会忽略不学渲染的元素(head display:none)
d、浏览器根据渲染树直接把页面绘制到屏幕上
浏览器容错进制
你从来没有在浏览器看过类似"语法无效"的错误,这是因为浏览器去纠正错误的语法,然后继续工作。
事件
当整个解析的过程完成以后,浏览器会通过DOMContentLoaded
事件来通知DOM
解析完成。
渲染堵塞
(栗子:当遇到script标签时,DOM构建暂停,直到脚本完成执行,再继续构建DOM树;当JS依赖CSS时,而CSS未被加载和构建,JS就会延迟脚本执行,直到CSS RULES被构建。)
CSS会堵塞JS执行
JS会堵塞后面的DOM解析
原则:
CSS资源排在JS资源的前面
JS放在HTML的底部,也就是</body>的前面
如果要改变阻塞模式,可以使用 defer 与 async
布局绘制
合成渲染层
回流重绘
浏览器编译执行
注:浏览器对同一域名的并发连接数是有限的,通常为 6 个。
附:https://blog.csdn.net/qq_39039128/article/details/104734337
参考:https://zhuanlan.zhihu.com/p/80551769
HTML资源加载顺序是什么?script标签的defer和async属性有什么作用?
html资源加载是从上至下的
- dom加载到link
dom和css的加载时并行的。当css加载时,dom也在加载构建。当css遇到img等标签时,会向浏览器发送一个请求后,继续加载。等资源回来后,再添加在dom相应的位置。 - dom加载script
dom和js不可以并行加载。当加载js时,遇到dom构建,dom构建就会暂停,直到js脚本执行完成才开始构建。当dom和js加载时,会将所有的js文件加载完做才能继续dom的加载。倘若js文件太大,则可能导致页面显示滞后,出现“假死”的状态,这叫做“堵塞效应”,非常不好的用户体验。所以js的文件开头一般需要window.οnlοad= function(){}或者$(document).ready(function(){})或者(function(){}()),就是为了让dom加载完再执行js。这样就不会出现找不到dom节点的问题了。
js堵塞资源加载的原因:浏览器防止js修改dom树,需要重新构建dom树的情况出现。 - 解决办法
前提:js是外部脚本
设置defer="true",dom和js可以并行加载,待页面加载完再执行js,不存在堵塞。
设置async="true",js是异步加载的,不依赖其他js和css,但是无法保证js文件的加载顺序,不存在堵塞,但是有跟dom并行的效果。
同时使用defer和async,defer会失效。
将script放在body标签后,就不会冲突了。
请说出静态资源请求的优化措施?是否用过“雪碧图”和iconfont?
- 不请求 cache
·尽量使用公共资源,设置缓存,不重新请求资源
·使用pwa的离线加载
·避免加载不存在的路径,避免耗费性能。(link的href,script的src)
·业务允许的情况下,做某些降级。例如弱网(2G、unknown)优先使用缓存。
·预解析DNS,浏览器提前获取静态资源的IP地址,避免请求时再发起DNS解析请求。
<meta http-equiv="x-dns-prefetch-control" content="on"><link rel="dns-prefetch" href="//cdn.domain.com">
·尽量不携带cookie(减少1kb)
·不做页面重定向,页面重定向延长页面内容返回的等待时长,一次重定向大约需要200ms不等的时间开销。
- 合并请求 combo
·cdn的combo技术合并资源
<script src="//cdn.domain.com/js/??a.min.js,b.min.js">
--合并拆分资源--
·使用webpack、rollup打包,打包js、打包图片到js(内嵌base64),生成雪碧图,甚至将项目打包成一个html(1kb)的文件
·拆分就是将开发使用的库,单独剥离开来,以公共资源引入
·将过大的文件拆分成多个小文件,避免单个资源耗时过大- 压缩资源
·前端打包压缩
·服务器打包压缩zip
·图片压缩(tiny)
·尽可能是html文件打包在1kb以内,使html的内容请求在一个RTT内请求完成,最高限度地提高html载入速度。通常情况下,tcp网络的最大传输单位是1500B- 静态资源分域存放
从不同域名请求资源。浏览器针对同一域请求数量要控制在6以内(并发数量),超过可能会造成堵塞。如果资源来自不同的域,可以增大并行请求和下载速度。- 延迟、异步、预加载、懒加载
·非首屏资源,使用defer或者async
·多屏页面,使用滚动加载
·首屏加载后可能会使用到的资源,在首屏加载完成后加载
·按需加载,执行的时候再加载- 参考https://www.jianshu.com/p/b114f60fce7a
如何利用浏览器的缓存机制?
(参考:https://www.jianshu.com/p/54cc04190252)
什么是SEO?如何优化?
seo就是搜索引擎优化。
- 提供页面加载速度
能使用css解决就不用背景图片;图片尽量压缩大小;图片的多个小icon可以制作成雪碧图,通过定位来展示;减少http的请求,加快页面加载的速度 - 结构、行为、表现的分离
使页面加载过慢的一个重要原因是:将css和js全部堆积在html上
可以使用外链引入来加快速度。
css放在head里面,js放在body的最下方 - 优化网站分级结构
使用面包屑导航,避免小蜘蛛迷路。最好每个页面都单独加sitemap,将网站结构一目了然地展示给小蜘蛛,更有利于小蜘蛛爬取信息 - 集中网站权重
小蜘蛛分配到页面的权重是一定,这些权重将平均分配到a链接。为了集中网站权重,可以使用“rel=nofollow”这个属性,告诉小蜘蛛,无须爬取该目标页,将权重分配给其他链接。 - 文本强调标签的使用
使用strong标签 - H标签的使用
使用h1时,一个页面最多只能有一个,因为它是自带权重的,一般放页面的标题(首页的logo也可以加h1标签) - 图片alt属性的使用
在图片没加载出来是,可以使用alt属性的文字代替,有利于小蜘蛛抓取信息 - a标签的title属性的使用
在不影响页面功能的情况下,a标签加上title属性,有利于小蜘蛛抓取信息 - 为图片指定宽高
在页面加载时,浏览器会为图片留下既定的位置,图片下面的代码可以直接下载而不用等待,提高并行下载速度,提高页面加载速度。 - 启用keep-alive属性
keep-alive---长连接。在没有设置此属性时,浏览器向服务器请求的connection是即连即断的。设置后,这个连接可以保持一段时间,提高页面加载速度。 - 使用浏览器缓存
为一些不经常变化的文件设置一个相对较长的过期时间,用户访问网站时,会缓存这些文件,下次请求时,缓存的文件不用再向服务器发送http请求了。减少http请求,提高页面加载速度。 - 启用gzip压缩
开启gzip压缩后,网站服务器传输数据前,经过了压缩;传输到浏览器时,数据会被解压缩,从而浏览器正常显示。但是压缩率提高了很多,并且搜索引擎对网站抓取量也提升了很多。
SPA(single page application,单页应用)和传统网页相比的优缺点是什么?
优点:
- 用户体验好、快,内容的改变不需要加载整个页面。避免不必要的跳转和重复渲染。
- 服务端压力小
- 前后端职责分离,架构清晰。前端负责交互逻辑,后端负责数据处理
缺点:
- 初次加载耗时长:加载页面时统一加载js、css文件,部分页面按需加载
- 前进后退路由管理:一个页面显示所有的内容,不能使用浏览器的前进后退功能
- SEO难度大:所有的内容在同一页面动态替换的,SEO难度大
跨域的定义是什么?
(参考:https://segmentfault.com/a/1190000011145364)
广义的跨域
一个域下的文档或脚本试图去请求另一个域下的资源
- 资源跳转:<a> 重定向 表单提交
- 资源嵌入:<script><link><frame><img>等dom标签,还有样式background:url() 、 @frot-face()等文件外链
- 脚本请求:ajax js或dom对象的跨域请求
狭义的跨域(通常是讲这个)---浏览器同源策略
如果缺少同源策略,浏览器很容易受到xss、csrf的攻击;
同源指的是“协议、域名、端口”三个相同;其中有一个不一样就是跨域,即使两个域名指向同一个ip也是跨域
同源限制以下几种行为:
- 无法访问cookie、localstorge、indexdb
- 无法获得js和dom对象
- 无法发送ajax请求
请说出常用的跨域解决方案
- jsonp
html下可以通过相应的标签从不同的域名加载静态资源
1、原生实现<script> var script script.type = 'text/javascript' script.src = "http://kyfeng.com?name=ky&callback=handleCallback" document.head.appendChild(script) //返回回调时执行函数 function handleCallback(res) { console.log(JSON.stringfy(res)) } </script> //服务器返回以下(返回时即执行全局函数) handleCallback({"status":true, "name":"ky"})
代码片段:
2、jquery ajax$.ajax({ type:'get', url:'http://kyfeng.com?name=ky', dataType:'jsonp', jsonCallback:'handleCallback', data:{} })
3、vue.js
this.$http.jsonp('http://kyfeng.com?name=ky',{ params:{}, jsonp:'handleCallback' }).then(res=>{ console.log(res) })
jsonp缺点:只能使用get请求
- document.domain+iframe
用于主域相同,子域不相同
原理:两个页面都通过js强制设置document.domain为基础主域,实现同域//父窗口 http://www.domain.com/a.html <iframe id="iframe" src="http://child.domain.com/b.html"></iframe> document.domain = "domain.com" var user = 'domain' //子窗口 http://child.domain.com/b.html document.domain = "domain.com" //获取父域变量 console.log(window.parent.user)
- location.hash+iframe
原理:不同域之间只能通过iframe的location.hash进行传值,只有同域才能通过js进行通信
a域想和b域通信,需要借助另一页面。 (A和C同域,B不同域)
A.html ----hash--- > B.html ---hash--->C.html---window.parent.parent执行A的callback--->A.html//A.html http://www.domain1.com/a.html <iframe id="iframe" src="http://www.domain2.com/b.html"> <script> var iframe = document.getElementById('iframe') setTimeout(function(){ iframe.src = iframe.src + '#name=domain' },1000) function onCallback(res) { console.log('接收的数据',res) } </script> //B.html <iframe id="iframe" src="http://www.domain1.com/c.html"> <script> var iframe = document.getElementById('iframe') window.onhashchange = function() { iframe.src = iframe.src + location.hash } </script> //C.html window.onhashchange = function() { window.parent.parent.onCallback('hello'+ location.hash.replace('#name=', '')) }
- window.name+iframe
window.name在不同页面(不同域名)加载后依然存在,并且支持设置很长的name(2MB)
//a.html http://www.domain1.com/a.html function proxy(url, callback) { let state = 0 let iframe = document.creatElement('iframe') iframe.src = url iframe.onload = function() { if(state === 1) { //读取同域window.name的数据 callback(iframe.contentWindow.name) destoryFrame() }else if(state === 0) { //加载跨域页成功后,切换同域代理 iframe.contentWindow.location = 'http://www.domain1.com/proxy.html' state = 1 } } document.body.appendChild(iframe) function destoryFrame() { iframe.contentWindow.document.write('') iframe.contentWindow.close() document.body.removeChild(iframe) } } proxy('http://www.domain2.com/b.html', function(res) { console.log(res) }) //proxy.html http://www.domain1.com/proxy.html //中间代理页 //b.html http://www.domain2.com/b.html window.name = 'this is domain2's name'
- postmessage (h5的api)
可解决的问题:
1、页面与新打开的页面数据传递
2、多页面的消息传递
3、页面嵌套iframe的消息传递
4、以上三种情形的跨域数据传递
用法:postMessage(data【-只支持字符串 最好使用JSON.stringify序列化一下】, origin【协议+主机+端口 | * | 同源设置为 / 】)//a.html http://www.domain1.com/a.html <iframe id="iframe" src="http://www.domain2.com/b.html"></iframe> <script> const iframe = document.getElementById('iframe') iframe.onload = function() { const data = { name:'ky' } iframe.contentWindow.postMessage(JSON.stringify(data), 'http://www.domain2.com') } window.addEventListener('message', funtion(res) { console.log(res) },false) </script> //b.html http://www.domain2.com/b.html window.addEventListener('message', funtion(e) { console.log('收到a传过来的数据',e.data) const data = JSON.parse(e.data) if(data) { data.number = 12 } window.parent.postMessage(JSON.stringify(data), 'http://www.domain1.com') },false)
- postmessage (h5的api)
- 跨域资源共享CORS
**********前端*********** //原生设置 xhr.withCredential = true //是否带cookie //jquery设置 $.ajax({ xhrFields:{ withCredential: true }, crossDomain: true // 让请求头中包含跨域的额外信息,但不会包含cookie }) //vue设置 1)axios axios.defaults.withCredentials = true 2)vue-resource Vue.http.options.credentials = true *********后端************ //Node res.header('Access-Control-Allow-Credentials', true) res.header('Access-Control-Allow-Origin', 'http://localhost:3001') res.header('Set-Cookie', 'l=a123456;Path=/;Domain=localhost:3001;HttpOnly') //HttpOnly的作用:让js无法读取cookie
- nginx代理跨域
1、nginx配置解决iconfont跨域(浏览器跨域访问js、css、img等静态资源被同源策略许可,但iconfont字体文件(eot|otf|ttf|woff|svg)例外,此时可在nginx的静态资源服务器加入以下配置)location / { add_header Access-Control-Allow-Origin *; }
2、nginx反向代理接口跨域
跨域原理:同源策略是浏览器的安全策略,不是HTTP协议的一部分。服务器调用HTTP接口这是使用HTTP协议,不会执行
JS脚本,不需要同源策略,也就不存在跨域问题。
#proxy服务器 server { listen 81; server_name www.domain1.com; location / { proxy_pass http://www.domain2.com:8080; #反向代理 proxy_cookie_domain www.domain2.com www.domain1.com; #修改cookie里域名 index index.html index.htm; # 当用webpack-dev-server等中间件代理接口访问nignx时,此时无浏览器参与,故没有同源限制,下面的跨域配置可不启用 add_header Access-Control-Allow-Origin http://www.domain1.com; #当前端只跨域不带cookie时,可为* add_header Access-Control-Allow-Credentials true; } }
- nodejs中间件跨域
1)非vue框架跨域
node + express + http-proxy-middleware搭建一个proxy的服务器
代码示例:
vue框架跨域(node + webpack + webpack-dev-server代理接口跨域)//前端 <script> var xhr = new XMLHttpRequest(); // 前端开关:浏览器是否读写cookie xhr.withCredentials = true; // 访问http-proxy-middleware代理服务器 xhr.open('get', 'http://localhost:3001/login?user=admin&callback=onCallback', true); xhr.send(); function onCallback(res) { console.log(res) } </script> //自己的服务器 3001 const express = require('express') const app = express() const path = require('path') const proxy = require('http-proxy-middleware') app.use('/', proxy.createProxyMiddleware({ // 代理跨域目标接口 target: 'http://localhost:3000', changeOrigin: true, // 修改响应头信息,实现跨域并允许带cookie onProxyRes: function(proxyRes, req, res) { res.header('Access-Control-Allow-Origin', 'http://localhost:3001'); res.header('Access-Control-Allow-Credentials', 'true'); }, // 修改响应信息中的cookie域名 cookieDomainRewrite: 'http://localhost:3001' // 可以为false,表示不修改 })) app.use(express.static(path.join(__dirname, 'www'))) app.listen(3001, function(err) { if(err) { throw new Error('监听失败', err) } console.log('服务器开启成功,端口为3001') }) //3000的服务器 const express = require('express') const app = express() const path = require('path') app.get('/login', function(req, res) { console.log(req.query) const { user, callback } = req.query const result = callback+"("+JSON.stringify({ "status": true, user })+")" // 向前台写cookie res.header('Set-Cookie', 'l=a123456;Path=/;Domain=localhost:3000;HttpOnly') //HttpOnly的作用:让js无法读取cookie res.status(200).send(result) }) app.listen(3000, function(err) { if(err){ throw new Error('监听失败', err) } console.log('服务器开启成功,端口为3000') })
webpack-dev-server----页面渲染服务和接口代理服务module.exports = { entry: {}, module: {}, ... devServer: { historyApiFallback: true, proxy: [{ context: '/login', target: 'http://www.domain2.com:8080', // 代理跨域目标接口 changeOrigin: true, secure: false, // 当代理某些https服务报错时用 cookieDomainRewrite: 'www.domain1.com' // 可以为false,表示不修改 }], noInfo: true } }
- websocket跨域
websocket protocol是h5一种新协议,实现浏览器和服务器全双工通信,同时允许跨域通讯,是server push技术的一种很好的实现。
原生 websocket api使用不方便,我们使用socket.io,它很好地封装了websocket接口,提供了更简单、灵活的接口,也对不支持websocket的浏览器提供了兼容。
<div>user input:<input type="text"></div> <script src="https://cdn.bootcss.com/socket.io/2.2.0/socket.io.js"></script> <script> const socket = io('http://localhost:3000') // 连接成功处理 socket.on('connect', function() { console.log('connecting') // 监听服务端消息 socket.on('message', function(msg) { console.log('data from server: ---> ' + msg); }) // 监听服务端关闭 socket.on('disconnect', function() { console.log('Server socket has closed.') }) }) document.getElementsByTagName('input')[0].onblur = function() { socket.send(this.value); } </script>
const http = require('http') const socket = require('socket.io') const server = http.createServer(function(req, res) { res.writeHead(200, { 'Content-type': 'text/html' }) res.end() }) server.listen(3000, function(err) { if(err){ throw new Error('监听失败', err) } console.log('服务器开启成功,端口为3000') }) // 监听socket连接 socket.listen(server).on('connection', function (client) { //接收信息 client.on('message', function (msg) { client.send('hello:' + msg) console.log('data from client: --->' + msg) }) // 断开处理 client.on('disconnect', function () { console.log('Client socket has closed') }) })
什么是CSRF攻击?如何预防?
(参考:https://www.cnblogs.com/hyddd/archive/2009/04/09/1432744.html)
- CSRF:跨站请求伪造。攻击者盗用用户身份,发送恶意请求,造成个人隐私泄露以及财产安全问题。
- 要完成一次CSRF,受害者必须完成:
1、登录并信任网站A,并且浏览器生成cookie
2、在未登出网站A的情况下,访问危险网站B -
CSRF源于web的隐式身份验证机制,web的身份验证机制可以识别出请求是来自某一用户的浏览器,但无法保证请求是经用户批准发送的。
防御机制(可从客户端和服务端两方面着手,但是服务端防御效果比较好,现在一般csrf防御都是在服务端进行)---在客户端页面增加伪随机数
- 直接受POST请求
- 验证码:需要用户互动,简单有效防御CSRF攻击
- 验证Referer:之前的网址一定保留在新页面的Referer属性中。通过检查Referer,可以判断请求是合法还是非法。但是服务器不是任何时候都接收到Referer;Referer Check一般用于监控CSRF攻击的发生,而不是抵御攻击
- Token:目前主流的做法是使用Token防御csrf攻击
1、Token要足够随机,使攻击者无法预测
2、Token是一次性的,即每次请求成功后要更新
3、Token要具有保密性,敏感操作使用POST,防止Token出现在URL中
什么是XSS攻击?如何预防?
- XSS是跨站脚本攻击,攻击者向有XSS漏洞的网站,注入恶意的HTML片段;当用户浏览该网站时,该HTML代码会自动执行,已达到攻击的目的;盗取用户的Cookie、破坏页面结构、重定向到其他网站等。
- 持久型XSS:对客户端攻击的脚本植入服务器,所有正常访问的用户都会受到XSS脚本的攻击
- 非持久型XSS:对一个页面的URL的某个参数做文章,把精心打包好的恶意脚本包装在URL参数中,再将该URL发布到网上,骗去用户访问,从而进行攻击
- 非持久型XSS安全威胁小,只要服务端调整业务代码进行过滤,这段XSS代码就会失效;持久型XSS威胁大,有时服务端要删好几张表,查询很多数据库才能把恶意代码删除
防御措施(防御XSS攻击最简单最直接的方法就是:过滤用户输入。)
- 如果不需要输入HTML,可以对用户输入的内容进行HTML转义
- 如果需要输入HTML,将用户的输入使用HTML解析库进行解析,并且获取其中的内容。然后根据用户原有的标签重构HTML元素树。在构建的过程中,所有的标签、属性都只从白名单中拿取。
请介绍一下cookies
cookie | localStorge | sessionStorge | session | |
存储位置 | 客户端 | 客户端 | 客户端 | 服务端 |
特点 | 随请求头每次提交 | 不随请求头提交,长时保存 | 不随请求头提交,页面关闭失效 | 安全 |
跨页 | 可跨页,不可跨域 | 可跨页,不可跨域 | 不可跨页,不可跨域 | 可跨页,不可跨域 |
大小 | 不超过4k | 可达5M | 可达5M |
key-value存储,同域名可用,不可跨浏览器
- cookie包括name、expires、domain、path等要素
- 设置cookie: document.cookie = "name="+name+";expires="+new Date().toUTCString()+";domain="+domain+";path="+path
- 读取cookie: const data = document.cookie //获取对应页面的cookie
解析cookie
var name = "jack"
var pwd = "123"
var now = new Date()
now.setTime(now.getTime() +1 * 24 * 60 * 60 * 1000)
var path = "/"
var document = {} //模拟一下
document.cookie = "name=" + name + ";expires=" + now.toUTCString() + ";path=" + path
function getKey(key) {
const data = document.cookie
const findStr = key + '='
const index = data.indexOf(findStr)
if(index === -1)
return null
const subStr = data.substring(index + findStr.length)
const subIndex = subStr.indexOf(';')
if(subIndex === -1)
return subStr
return subStr.substring(0, subIndex)
}
const result = getKey('name')
console.log(result)
正则
function getKey(key) {
return JSON.parse("{\"" +document.cookie.replace(/;\s+/gim, "\",\"").replace(/=/gim, "\":\"") + "\"}")[key];
}
- 清除cookie
document.cookie = "name="+" ;expires="+new Date().toUTCString()
请说出2个以上post请求的数据格式
-
application/json
-
text/plain
-
application/x-www-form-urlencoded
-
multipart/form-data
get和post的区别?还有其他method吗?
- get不安全,请求参数拼接在url。post对于用户来说是不可见的。
- get数据量小,受url长度限制。post数据量较大,一般被默认不受限制。
- get限制form数据集的值为ASCII字符。post支持整个ISO10646字符集。
- get执行效率比post好。get是form提交的默认方法。
如何用Promise改造ajax?
(Promise,就是一个对象,用来传递异步操作的信息。)
const ajaxPromise = params => {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest()
xhr.open(params.type||'get', params.url, true)
xhr.send(params.data||null)
xhr.onreadystatechange = ()=>{
if(xhr.readyState === 4 ) {
if(xhr.status === 200){
resolve(xhr.responseText)
}else {
reject(xhr.responseText)
}
}
}
})
}
Promise除了then,还有什么方法?
catch resolve reject all race
如何实现多个异步请求全部完成后,再执行某操作?
Promise.all([p1, p2, p3]) 批量执行 现多个异步请求全部完成后,再执行某操作
Promise.race() 类似于Promise.all() ,区别在于它有任意一个完成就算完成
是否用过async/await?
async function fn() {
await f1() //同步
console.log('xx') //异步
}
function f1(){}