前端面试 · 网络相关

1,201 阅读3分钟

目前面了蘑菇街、阿里、腾讯、滴滴的前端实习,总结一下碰到的网络有关问题(附答案)。 问题集:前端小白的面试问题集

1.跨域问题是怎么产生的,如何解决?

跨域问题的产生
跨域发生的场景一般分为以下几类: 跨域情况 这是浏览器的同源策略所导致,所谓同源是指"协议+域名+端口"三者相同,即便两个不同的域名指向同一个ip地址,也非同源。浏览器引入同源策略主要是为了防止XSS,CSRF攻击(问题2将详细解释)。 在同源策略影响下,域名A向域名B发送Ajax请求,或操作Cookie、LocalStorage、indexDB等数据,或操作dom,js就会受到限制,但请求css,js等静态资源不受限制。 浏览器同源策略 解决方案
关键词:JSONP;CORS;nigix;webpack;WebSocket;postMessage;document.domain;window.name;local.hash

  • ①JSONP(简便)
    jsonp的跨域原理是通过在html中动态添加script标签,返回的数据包括函数调用,实现跨域,原生代码如下:
#www.taobao.com下的一个html
<script>
    var script = document.creatElement('script');
    script.type = 'text/javascript';
    script.src = 'http://www.baidu.com/getdata?callback=demo';
    #返回的字符串为demo({msg: 'helloworld'}),解析后执行demo函数
    
    function demo(res){
      console.log(res.msg);
    }
</script>

当然jquery里的ajax也有封装好的方法,实现起来更方便:

$.ajax({
    url:'http://www.baidu.com/getdata',
    type:'get',
    dataType: 'jsonp',  // 请求方式为jsonp
    jsonpCallback: 'demo', // 自定义回调函数名
    data: {}
});
  • ②CORS(常用)
    跨域资源共享(CORS, Cross-Origin Resource Sharing) 是一种机制,它使用额外的 HTTP 头来告诉浏览器 让运行在一个 origin (domain) 上的Web应用被准许访问来自不同源服务器上的指定的资源。当一个资源从与该资源本身所在的服务器不同的域或端口请求一个资源时,资源会发起一个跨域 HTTP 请求。 目前,所有浏览器都支持该功能,IE浏览器不能低于IE10。 以Node服务器代码为例,设置CORS样例如下:
var express=require('express');
var url=require('url');
var app=express();
var allowCrossDomain = function(req, res, next) {
    #设置cors
    res.header('Access-Control-Allow-Origin', 'http://localhost:63342');
    res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');
    res.header('Access-Control-Allow-Headers', 'Content-Type');
    res.header('Access-Control-Allow-Credentials','true');
    next();
};
app.use(allowCrossDomain);
app.get('/getData',function (req,res,next) {
    var queryValue=url.parse(req.url).query;
    if(queryValue==='fortunewheel@sina.com'){
        res.send(true);
    }else {
        res.send(false);
    }

});
app.listen(3001);

过程: 流程图如下: 简单请求条件:

1) 请求方法是以下三种方法之一:
HEAD
GET
POST
(2)HTTP的头信息不超出以下几种字段:
Accept
Accept-Language
Content-Language
Last-Event-ID
Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain

对于简单的请求方法:

以 CORS 中的 GET 请求为例,当浏览器发起请求时,请求头中携带了如下信息:

...
Host: localhost:8080 
Origin: http://localhost:8081 
Referer: http://localhost:8081/index.html
...

假如服务端支持 CORS,则服务端给出的响应信息如下:

...
Access-Control-Allow-Origin: http://localhost:8081 
Content-Length: 20 
Content-Type : text/plain;charset=UTF-8 
Date: Thu, 12 Jul 2018 12:51:14 GMT
...

响应头中有一个 Access-Control-Allow-Origin 字段,用来记录可以访问该资源的域。当浏览 器收到这样的响应头信息之后,提取出 Access-Control-Allow-Origin 字段中的值,发现该值包含当前页面所在的域,就知道这个跨域是被允许的,因此就不再对前端的跨域请求进行限制。这就是 GET 请求的整个跨域流程,在这个过程中,前端请求的代码不需要修改,主要是后端进行处理。

  • 对于复杂请求

1.发送一个 OPTIONS 请求。代码如下:

...
Access-Control-Request-Method DELETE 
Connection keep-alive 
Host localhost:BOBO 
Origin http://localhost: 8081
...

这个请求将向服务端询问是否具备该资源的 DELETE 权限,服务端会给浏览器一个响应,代码如下:

...
HTTP/1.1 200
Access-Control-Allow-Origin: http://localhost:8081 
Access-Control-Allow-Methods: DELETE
Access-Control-Max-Age: 1800 
Allow: GET, HEAD, POST, PUT, DELETE, OPTIONS, PATCH 
Content-Length: 0 
Date: Thu, 12 Jul 2018 13:20:26 GMT
...

服务端给浏览器的响应, Allow 头信息表示服务端支持的请求方法,这个请求相当于一个探测请求,当浏览器分析了请求头宇段之后,知道服务端支持本次请求, 则进入第二步。 2.发送 DELETE 请求。接下来浏览器就会发送一个跨域的 DELETE 请求,代码如下:

...
Host: localhost:8080 
Origin: http://localhost:8081 
Connection: keep-alive 
...

服务端给出响应

...
HTTP/1.1 200
Access-Control-Allow-Origin: http://localhost:8081
Content-Type: text/plain;charset=UTF-8
Date: Thu, 12 Jul 2018 13:20:26 GMT
...

注意:这里还有一处需要前端工程师协作的地方就是cookie的传递,默认情况下通过CORS这样的方式是不会传递cookie.一般强制性将cookie添加到header的做法,也会被浏览器拒绝并报错.在服务器端会通过添加一个response头,Access-Control-Allow-Credentials来控制是否允许Cookie的提交。

  • ③nigix反向代理(常用)
    在服务器端配置路由规则,服务器代理样例如下:
server{
    # 监听8080端口
    listen 8080;
    # 域名是localhost
    server_name localhost;
    #凡是localhost:8080/api这个样子的,都转发到真正的服务端地址http://www.baidu.com:8080 
    location ^~ /api {
        proxy_pass http://www.baidu.com:8080;
    }    
}
  • ④webpack的Porxy代理(常用)
    webpack代理设置代码样例:
module.exports = {
  //...
  devServer: {
    proxy: {
      '/api': {
        target: 'http://www.baidu.com/',
        pathRewrite: {'^/api' : ''},
        changeOrigin: true,     // target是域名的话,需要这个参数,
        secure: false,          // 设置支持https协议的代理
      },
      '/api2': {
          .....
      }
    }
  }
};
  • ⑤postMessage
    a网页用<iframe src="">标签引入b网页,然后使用postMessage语句发送数据,b网页监听到事件后返回数据。
#a网页
var iframe = document.getElementById('iframe');
iframe.onload()={
iframe.contentWindow.postMessage(JSON.stringify(data),'http://www.b.com');
}
 window.addEventListener('message', function(e) {
      ...
    }, false);
#b网页
window.onmessage = function(e){
    #window.parent.postMessage(...)
}
  • ⑥Websocket
    WebSocket 协议在2008年诞生,2011年成为国际标准。所有浏览器都已经支持了。 相比于http协议,websoket协议给我们带来了极大的方便,举个例子: 之前,我们在做消息通知的时候,需要设置定时器,频繁的向后台发起异步ajax请求实现长轮询,获取最新的数据,这样效率非常低,非常浪费资源,因为不停的发起http请求,不停的与服务端建立连接,或者http链接始终打开。 而现在有了websocket,就解决了轮询的问题,websocket只需要建立一次连接,就可以保持长久连接,相比轮询不停的建立连接,大大的提升了效率,这样只要服务端有数据变化,就可以立即通知前台了。
    WebSocket天然支持跨域,只需要利用Socket.IO等软件包实现服务器端和客户端的互相监听。
  • ⑦Docment.domain
    这种方式只适合主域名相同,但子域名不同的iframe跨域。 实现原理:两个页面都通过js强制设置document.domain为基础主域,就实现了同域。
<script>
document.domain = 'a.com';
<script>
  • ⑧window.name
    当在浏览器中打开一个页面,或者在页面中添加一个iframe时即会创建一个对应的window对象,当页面加载另一个新的页面时,window.name的属性是不会变的。这样就可以利用在页面动态添加一个iframe然后加载数据页面 b给a的信息都存在window.name中(需要把地址指向同源)
  • ⑨local.hash(不常用)
    Local.hash 发送页面通过iframe引用中间页面并发送hash值,中间页面监听hash值改变并传值给子页面,子页面调用发送页面的回调方法。

2.Ajax有哪些传输方法?

关键词:ajax();get();post();getJSON();
①$.ajax()返回其创建的XMLHttpRequest对象
ajax()只有一个参数:参数key/value对象,包含各配置及回调函数信息。
如果你指定了dataType选项,那么需要保证服务器返回正确的MIME信息,(如 xml 返回 "text/xml")。

$.ajax({
type: "post",
dataType: "html",
url: '/Resources/GetList.ashx',
data: dataurl,
success: function (data) {
 ...
}
});

②$.get()请求载入信息
GET请求功能简化了ajax(),请求成功时可调用回调函数。当然如果需要在出错时执行函数,那么还请使用ajax()。

$.get("test.cgi", { name: "John", time: "2pm" },
function(data){
                ...
});

③$.post()请求载入信息
与GET请求一样,POST也是请求内容与回调函数组成

$.post("test.cgi", {  name: "John", time: "2pm" }, 
function (data) {
                ...
}
})

④$.getJSON() GET请求载入 JSON 数据
通过GET请求,解析json数据

$.getJSON("http://api.flickr.com/services/feeds/",
function(data){

                ...
});

3.Post和Get方法有哪些区别?

关键词:大小;安全性;次数
①表层区别
(a) get是从服务器上获取数据,post是向服务器传送数据;
(b) get参数通过URL传递,post放在Request body中;
(c) get传送的数据量较小,不能大于2KB。post传送的数据量较大,一般被默认为不受限制。但理论上,IIS4中最大量为80KB,IIS5中为100KB。(这里有看到其他文章介绍get和post的传送数据大小跟各个浏览器、操作系统以及服务器的限制有关)
(d) get在浏览器回退时是无害的,而post会再次提交请求;
(e) get请求会被浏览器主动cache,而post不会,除非手动设置;
(f) 对参数的数据类型,get只接受ASCII字符,而post没有限制。
②中层区别
HTTP的底层是TCP/IP,GET和POST的底层都是TCP/IP,也就是说,GET/POST都是TCP链接。GET和POST能做的事情是一样一样的。你要给GET加上request body,给POST带上url参数,技术上是完全行的通的。
③深层区别
对于GET方式的请求,浏览器会把http header和data一并发送出去,服务器响应200(返回数据);
而对于POST,浏览器先发送header,服务器响应100 continue,浏览器再发送data,服务器响应200 ok(返回数据)
不过并非所有浏览器都要发送两次Post,Firefox只发送一次。

4.Vue如何实现按需加载?

关键词:component;import;ensure;
①Vue异步组件
vue-router配置路由,使用vue的异步组件技术,可以实现按需加载。但是,这种情况下一个组件生成一个js文件。举例如下:

    {
            path: '/promisedemo',
            name: 'PromiseDemo',
            component: resolve => require(['../components/PromiseDemo'], resolve)
        }

②Es6:import()

const ImportFuncDemo1 = () => import('../components/ImportFuncDemo1')

③webpack提供的require.ensure()
vue-router配置路由,使用webpack的require.ensure技术,也可以实现按需加载。 这种情况下,多个路由指定相同的chunkName,会合并打包成一个js文件。 举例如下:

{
            path: '/promisedemo',
            name: 'PromiseDemo',
            component: resolve => require.ensure([], () => resolve(require('../components/PromiseDemo')), 'demo')
}

5.nigix怎么配置路由?

关键词:全局;events;http;server;location
(a) 全局块:配置影响nginx全局的指令。一般有运行nginx服务器的用户组,nginx进程pid存放路径,日志存放路径,配置文件引入,允许生成worker process数等。
(b) events块:配置影响nginx服务器或与用户的网络连接。有每个进程的最大连接数,选取哪种事件驱动模型处理连接请求,是否允许同时接受多个网路连接,开启多个网络连接序列化等。 (c) http块:可以嵌套多个server,配置代理,缓存,日志定义等绝大多数功能和第三方模块的配置。如文件引入,mime-type定义,日志自定义,是否使用sendfile传输文件,连接超时时间,单连接请求数等。 (d) server块:配置虚拟主机的相关参数,一个http中可以有多个server。 (e)location块:配置请求的路由,以及各种页面的处理情况。

#配置页结构
...              #全局块

events {         #events块
   ...
}

http      #http块
{
    ...   #http全局块
    server        #server块
    { 
        ...       #server全局块
        location [PATTERN]   #location块
        {
            ...
        }
        location [PATTERN] 
        {
            ...
        }
    }
    server
    {
      ...
    }
    ...     #http全局块
}

6.浏览器有哪些缓存方式?CDN是怎么缓存的?

浏览器缓存: 图解浏览器缓存 缓存判断过程:

CDN缓存:
而对于一些用户访问量巨大的网站而言,如果所有用户都去服务器请求数据,服务器会很快崩溃,并且在不同网络以及不同地区的用户,请求服务器的速度也不一样。为了提高这部分用户的访问速度,CDN 提出创建一些最接近用户网络的边缘服务器,然后将文件缓存在这些边缘服务器(节点)上,这就是 CDN 缓存。
一般HTTP请求过程: HTTP请求 接入CDN后的请求过程: 接入CDN后

7.web攻击有哪些方式?怎么解决?

关键词:XSS;CSRF;

  • ①xss攻击
    跨站脚本漏洞(xss)是通过对网页注入可执行代码,成功地被浏览器执行,达到攻击的目的。有两种类型:
    存储型:将脚本作为数据存到服务器,其他用户读取时执行
    反射型:在get请求的url里输入脚本语句
方案:过滤特殊字符,HttpOnly 浏览器禁止页面的JS访问带有HttpOnly属性的Cookie。
  • ②CSRF攻击
    跨站请求伪造(CSRF):攻击者诱导受害者进入第三方网站,在第三方网站中,向被攻击网站发送跨站请求。利用受害者在被攻击网站已经获取的注册凭证,绕过后台的用户验证,达到冒充用户对被攻击的网站执行某项操作的目的。
方案:
1:表单生成tocken放入cookie或者session(所有表单都包含同一个伪随机值),服务端进行hash值验证
2.验证码,用户加上图片生成的验证码
  • ③网络劫持攻击
    网络劫持攻击主要是通过一些代理服务器,或者wifi等有中间件的网络请求,进行劫持,不法分子通过这种方式获取到用户的信息
方案:这种通过请求网络地址攻击的我们可以通过对http进行加密来防范,
这样不法分子即使或得到,也无法解密。
  • ④控制台注入代码
    不法分子通过各种提示诱骗用户在控制台做一些操作,从而获取用户信息。
方案:在控制台对用户进行友好的提示,不要轻易相信提示。
  • ⑤钓鱼
    钓鱼攻击是获得用户敏感信息的一种方法。钓鱼攻击的目标通常是在线银行用 户、PayPal、eBay等。
方案:
1.SPF:为了防范垃圾邮件而提出来的一种DNS记录类型,它是一种TXT类型的记录,它用于登记某个域名拥有的用来外发邮件的所有IP地址,从而验证网站合法性。
2.SafeBrowsing API:谷歌的可访问API,允许浏览器在渲染之前检测URL的正确性。
  • ⑥DDoS
    分布式拒绝服务攻击(DDOS),简单说就是发送大量请求是使服务器瘫痪。DDos攻击是在DOS攻击基础上的,可以通俗理解,dos是单挑,而ddos是群殴,因为现代技术的发展,dos攻击的杀伤力降低,所以出现了DDOS,攻击者借助公共网络,将大数量的计算机设备联合起来,向一个或多个目标进行攻击。
方案:
1.增加带宽:但是攻击者用各地的电脑进行攻击,他的带宽不会耗费很多钱,但对于服务器来说,带宽非常昂贵。
2.云服务:一般云服务提供商有自己的一套完整DDoS解决方案,并且能提供丰富的带宽资源。
  • ⑦SQL注入攻击
    攻击者向服务器提交恶意的sql代码,导致源程序执行包含恶意代码的sql(sql字符串拼接)
方案:sql预编译,将参数传入而不是整个提交字符串传入
  • ⑧点击劫持
    点击劫持是指在一个Web页面中隐藏了一个透明的iframe,用外层假页面诱导用户点击,实际上是在隐藏的frame上触发了点击事件进行一些用户不知情的操作。
方案:网站可以通过设置 X-Frame-Options阻止站点内的页面被其他页面嵌入从而防止点击劫持

8.浏览器本地存储方案

  • ①LocalStorage
    LocalStorage就是Key-Value的简单键值对存储结构,Web Storage除了localStorage的持久性存储外,还 有针对本次回话的sessionStorage方式,一般情况下localStorage较为常用。
  • ②cookie
    这种存储方式存储内容很有限,只适合做简单信息存储。
  • ③Indexed Database API
    IndexedDB可以存储结构对象,可构建key和index的索引方式查找,目前各浏览器的已经逐渐支持
  • ④FileSystem API
    FileSystem API相当于操作本地文件的存储方式,目前支持浏览器不多,其接口标准也在发展制定变化中 ,因此也可以动态生成图片到本地文件,然后通过 filesystem:http: 的URL方式直接赋值给img的 html元素的src访问。
  • ⑤Application Cache
    window.applicationCache 对象是对浏览器的应用缓存的编程访问方式。

9.浏览器的渲染机制

  • 加载时的流程
  • 渲染过程
  • 交互时的流程

关于阻塞问题:

  • CSS不会阻塞DOM解析,但会阻塞DOM渲染。
  • CSS会阻塞JS执行,并不会阻塞JS文件下载
  • JS脚本加载会阻塞页面渲染

由浏览器渲染流程我们可以看出:

  • DOM 和 CSSOM通常是并行构建的,所以「CSS 加载不会阻塞 DOM 的解析」。

  • 然而由于Render Tree 是依赖DOM Tree和 CSSOM Tree的,所以它必须等到两者都加载完毕后,完成相应的构建,才开始渲染,因此,「CSS加载会阻塞DOM渲染」。

  • 由于 JavaScript 是可操纵 DOM 和 css 样式 的,如果在修改这些元素属性同时渲染界面(即 JavaScript 线程和 UI 线程同时运行),那么渲染线程前后获得的元素数据就可能不一致了。因此为了防止渲染出现不可预期的结果,浏览器设置 「GUI 渲染线程与 JavaScript 引擎为互斥」的关系。

  • 「有时候JS需要等到CSS的下载,这是为什么呢?」

仔细思考一下,其实这样做是有道理的,如果脚本的内容是获取元素的样式,宽高等CSS控制的属性,浏览器是需要计算的,也就是依赖于CSS。浏览器也无法感知脚本内容到底是什么,为避免样式获取,因而只好等前面所有的样式下载完后,再执行JS。 JS文件下载和CSS文件下载是并行的,有时候CSS文件很大,所以JS需要等待。 因此,样式表会在后面的 js 执行前先加载执行完毕,所以「css 会阻塞后面 js 的执行」。

10.渲染过程有哪些不在文档流中

CSS解析时属性为浮动的、绝对定位的。

11.cookie,session,token

11.1 简介

推荐查看!:彻底理解session、cookie和token

三者均表明身份信息,当次请求由谁发起

特点:

  • cookie:存放在硬盘文件中(浏览器相关);
  • session:存放在服务器内存中,退出后被清空,用户每次发送session_id;
  • token:服务端生成后,不保存,发送给客户端,客户端的header里每次请求携带

11.2 token的安全问题:

token在网络中传输,很容易被中间人获取,(另外用户id也很容易获取),进而模拟用户进行其他相关操作。 解决办法:

1.密钥加密

  • 服务器端

响应头增加随机字符串 CSRF_TOKEN=xxxxxxxxxxx(每次请求都不同)

  • 客户端

客户端和服务端 保留密钥 secret = yyyyyyyyy

客户端获取响应头CSRF_TOKEN下次请求必须携带

客户端 (secret+提交内容) 进行签名

2.token与ip绑定

防止token被截取后在别的网络环境发出请求,在服务器通过请求ip与这个ip必须对上才能解密,但是会损失一定用户体验

3.加时间限制

本次请求从客户端到服务在1分钟以内在效(如果客户端token失败,重试一次,可能遇到跨分钟的时候)

11.3 Token的存储位置

  • csrf 攻击无法获取第三方的 cookie,而是直接使用 cookie进行查询的时候会自动携带 cookie。
  • xss攻击通过代码注入(脚本)可以获取 cookie。需要设置转义。

1.客户端使用 cookie直接认证,需要设置 cookie为 httpOnly,可以防止 xss攻击。但是无法防止 csrf攻击。需要设置伪随机数 X-XSRF-TOKEN。(推荐!不需要处理 xss,并且xsrf 随机数有完善的应用机制)

2.客户端使用 auth授权头认证,token存储在 cookie中,需要防止xss攻击。可以防止 csrf攻击,因为 csrf只能在请求中携带 cookie,而这里必须从 cookie中拿出相应的值并放到 authorization 头中。实际上cookie不能跨站(同源政策)被取出,因此可以避免 csrf 攻击。(适用于 ajax请求或者 api请求,可以方便的设置 auth头)

3.可以将token存储在 localstorage里面,需要防止xss攻击。实现方式可以在一个统一的地方复写请求头,让每次请求都在header中带上这个token, 当token失效的时候,后端肯定会返回401,这个时候在你可以在前端代码中操作返回登陆页面,清除localstorage中的token。(适用于 ajax请求或者 api请求,可以方便的存入 localstorage) 对于单页应用适合采用sate+localstorage:因为state是单页的,为了同一个浏览器打开第二个网页而不需要登录,所以要使用cookie或者localstorage存储,为什么不选用cookie:1,cookie存储量小;2,cookie存储个数有限;3,其实是最重要的 请求时会带上cookie,增加网络负担,所以建议用state+localStorage,当然要处理好加密,过期等问题.

11.4 Cookie安全性问题: www.jianshu.com/p/e78cd310b…

Cookie篡改

这是最容易出现的情况,也就是直接修改 Cookie 的内容。我们知道,Cookie 是存储在客户端的,所以里面的内容可以通过浏览器的控制台或者 js 代码进行修改的。因此 Cookie 相对而言是不可信的,如果我们把一些重要的信息,例如权限信息存在 Cookie,很容易就被有心人篡改,导致越权问题。

Cookie 劫持

很多网站是用 Cookie 来做用户识别的,例如 Cookie 里存储了 Session id,通过Session id 来获取更进一步的用户信息。因此如果用户的 Cookie 被劫持了,盗用者是可以用这个 Cookie 来伪装成该用户来进行网站操作的。Cookie 劫持一般和 XSS 攻击一起使用,先通过 XSS 攻击获得了在页面上运行 js 的能力,然后通过 js 读取 Cookie,并发送给远程的服务器。例如

除了通过 XSS 攻击来劫持 Cookie 外,攻击者还可能在网络节点中嗅探到 HTTP 连接的 Cookie。HTTP 连接采用的是明文传输,相当于所有信息都是在网络上裸奔的,很容易就会被有心人窃取到了。

Cookie 作用域攻击

Cookie 有两个很重要的属性:Domain 和 Path,用来指示 Cookie 的作用域。不同作用域下的 Cookie 可以同时存在,例如我们可以有一个 a.b.com下的 Cookie aaa,也可以同时有一个*.b.com下的 Cookie aaa。前者在 a.b.com下生效,后者在b.com的所有子域下生效。

在某些情况下,如果我们的 Cookie 作用域设置得太高,即使我们开启了 Cookie 安全选项,攻击者仍可能通过设置一个更小作用域的 Cookie 来覆盖掉我们设置的 Cookie,使得在这个小作用域内,生效的不是我们设置的 Cookie,而是攻击者设置的 Cookie。

介绍了这么多不正确地使用 Cookie 带来的问题,那我们该如何来解决呢?

Cookie安全防范

  • 不放重要数据,重要数据放Session。我们已经知道 Cookie不安全了,就不要作死非把重要数据放在 Cookie 里了
  • Cookie 数据加签名。对 Cookie 数据添加签名,这样 Cookie 如果被篡改了,服务端使用的时候通过校验签名就能发现了。
  • Cookie数据加密。加密后数据就很难篡改了,但是加解密过程会带来性能损耗,这个就要进行衡量了。
  • 开启 Cookie 的 httponly 参数,让 Cookie 只能在网络请求中被传输,避免js操作和获取 Cookie
  • 全站 HTTPs + Cookie secure 设置。避免 Cookie 在网络传输过程中被劫持。仅仅设置全站 HTTPs 是不够的,还得开启 Cookie secure 设置,否则当secure的限制没有开启时,那么在一个HTTPs 的网站中,一个 XSS 攻击还是能通过 HTTP 读取到 HTTPs 下的 Cookie。 设置更小作用域的 Cookie,尽可能给 Cookie 设置合适且小的作用域。

12.TCP/IP四层模型

1.链路层(数据链路层/网络接口层):包括操作系统中的设备驱动程序、计算机中对应的网络接口卡

2.网络层(互联网层):处理分组在网络中的活动,比如分组的选路。

3.运输层:主要为两台主机上的应用提供端到端的通信。

4.应用层:负责处理特定的应用程序细节。

13.HTTP2.0 文件传输方式

HTTP2.0核心:

①二进制传输

HTTP 2.0中所有加强性能的核心带你在于此--二进制传输。

之前的HTTP的版本中,我们传输数据方式--文本传输。

在HTTP 2.0中引入了新的编码机制,所有传输的数据都会被分隔,并采用二级制格式编码。

②多路复用

在 HTTP 2.0 中,有两个非常重要的概念,分别是帧(frame)和流(stream)。

帧代表着最小的数据单位,每个帧会标识出该帧属于哪个流,流也就是多个帧组成的数据流。

多路复用,就是在一个 TCP 连接中可以存在多条流。换句话说,也就是可以发送多个请求,对端可以通过帧中的标识知道属于哪个请求。通过这个技术,可以避免 HTTP 旧版本中的队头阻塞问题,极大的提高传输性能。

③header压缩

在 HTTP 1.X 中,我们使用文本的形式传输 header,在 header 携带 cookie 的情况下,可能每次都需要重复传输几百到几千的字节。

在 HTTP 2.0 中,使用了 HPACK 压缩格式对传输的 header 进行编码,减少了 header 的大小。并在两端维护了索引表,用于记录出现过的 header ,后面在传输过程中就可以传输已经记录过的 header 的键名,对端收到数据后就可以通过键名找到对应的值。

③服务端push

在 HTTP 2.0 中,服务端可以在客户端某个请求后,主动推送其他资源。

可以想象以下情况,某些资源客户端是一定会请求的,这时就可以采取服务端 push 的技术,提前给客户端推送必要的资源,这样就可以相对减少一点延迟时间。当然在浏览器兼容的情况下你也可以使用 prefetch 。

④QUIC

这是一个谷歌出品的基于 UDP 实现的同为传输层的协议,目标很远大,希望替代 TCP 协议。

1、该协议支持多路复用,虽然 HTTP 2.0 也支持多路复用,但是下层仍是 TCP,因为 TCP 的重传机制,只要一个包丢失就得判断丢失包并且重传,导致发生队头阻塞的问题,但是 UDP 没有这个机制

2、实现了自己的加密协议,通过类似 TCP 的 TFO 机制可以实现 0-RTT,当然 TLS 1.3 已经实现了 0-RTT 了

3、支持重传和纠错机制(向前恢复),在只丢失一个包的情况下不需要重传,使用纠错机制恢复丢失的包。纠错机制:通过异或的方式,算出发出去的数据的异或值并单独发出一个包,服务端在发现有一个包丢失的情况下,通过其他数据包和异或值包算出丢失包。在丢失两个包或以上的情况就使用重传机制,因为算不出来了。

14.HTTP数据劫持

HTTP的劫持分两种第一种是DNS劫持,第二种是内容劫持,后者是基于前者的基础上发展出来,是比较高级的劫持手段,目前无解,下面来分开讲解:

DNS劫持:

DNS劫持又称域名劫持,是指在劫持的网络范围内拦截域名解析的请求,分析请求的域名,把审查范围以外的请求放行,否则返回假的IP地址或者什么都不做使请求失去响应,其效果就是对特定的网络不能访问或访问的是假网址。其实本质就是对DNS解析服务器做手脚,或者是使用伪造的DNS解析服务器可以通过下图来展示

解决办法

DNS的劫持过程是通过攻击运营商的解析服务器来达到目的。我们可以不用运营商的DNS解析而使用自己的解析服务器或者是提前在自己的App中将解析好的域名以IP的形式发出去就可以绕过运营商DNS解析,这样一来也避免了DNS劫持的问题。

内容劫持:

内容劫持一开始的出发点是好的,是运营商为了加快用户的访问速度同时减少自己的流量损耗而做的一个缓存机制,用户在像服务器请求数据的时候运营商会把用户的请求转移到这个缓存池中,如果缓存中有就直接返回,没有的话再去像服务器请求然后拦截并缓存服务端给用户的回调数据,这样一来可以极大的降低运营商像服务器请求的次数,也能加快用户的访问,所以出发点是好,但是一些非法的商家对缓存池内部做一次些处理就是直接对返回的内容进行修改,这样一来我们就会接受到错误的数据

解决办法:

现在暂时没有发现办法,不过这样的劫持不是很多。

15.封装带过期时间的localStorage

// 封装一个localStorage函数,减少页面请求
export function Storage() {
    if (!window.localStorage) {
        // 这里应该走cookie逻辑
        alert('此版本浏览器不支持本地存储')
        return false
    }
    return {
        set(key, obj, outtime) {
            let ctime = parseInt(Date.now() / 1000) //获取存数据的时间
            let exp = outtime || 24 * 60 * 60//outtime 是 秒为单位的过期时间
            let outObj = { // 时间和数据
                outime: ctime + exp,
                data: obj
            }
            localStorage.setItem(key, JSON.stringify(outObj))
        },
        get(key) {
            let data = JSON.parse(localStorage.getItem(key))
            //初始化没有数据
            if (!data) { return false }
            //判断过期时间 和获取数据的时间对比 大于过期时间说明超时
            if (data.outime >= parseInt(Date.now() / 1000)) {
                return data.data
            } else {
                return false
            }
        }
    }
}

16.HTTPS 解决了什么问题?过程如何?

解决问题:

  • 防止第三方冒充服务器。
  • 防止第三方拦截通信报文,窃取通信中请求报文、响应报文的内容。
  • 防止第三方拦截通信报文,篡改报文内容。

过程:

  • 服务提供者向办证机构申请证书。
  • 将证书发布到web服务中
  • 客户端向服务端发起申请,并发起一个随机数a。
  • 服务端将证书返回客户端证明自己,并发会一个随机数b。
  • 客户端根据证书的颁发机构,拿到预装的公钥,对其摘要进行解密
  • 客户端根据证书中的摘要算法,进行客户端的摘要计算,并将计算结果和解密后的摘要进行比较 如果不一致,说明证书是伪造或被篡改过,立即停止通信
  • 如果一致,则生成第三个随机数c,并用a、b、c生成对称加密算法的密钥,并用证书中的公钥(对应服务其的密钥)对c进行加密,然后将c发给服务端
  • 服务端用密钥将c解密,并用a、b、c生成对称加密算法的密钥。
  • 之后的通信将报文进行签名,并与报文一起进行对称加密(防止他人恶意篡改信息,以试错方式进行密钥的破解,以增加破解难度)

17.sessionStorage的作用范围

  • 通过右键单击打开链接时 – > “在新标签中打开链接”,sessionStorage在TargetPage上为空.
  • 但是当用正常的左键单击打开链接时,sessionStorage会被复制到TargetPage.

也就是说打开新的标签页,sessionStorage无法共享,但是从页面中打开链接,可以共享。

18.前端如何进行数据/错误上报

对业务逻辑的执行收集了日志数据之后可以参数的形式构造一个url,再通过一个Image请求发送到到服务器就完成了日志的上报。

(new Image).src = `/r.png?page=${location.href}&amp;param1=${param1}...`;

有时为了隔离业务与数据,需要对另一个域名发送请求,这里前端在进行日志上报的时候要添加避免跨域标识,如fetch方式:

var url = 'https://arms-retcode.aliyuncs.com/r.png';
fetch(
`${url}?t=perf&amp;page=qar.alibaba-inc.com&amp;load=1168`,
{mode:'no-cors'}
)

url本身存在长度限制,如果数据量较大,可考虑直接发送请求,并对请求做相关优化:

  • 压缩请求报文:压缩请求头(http2),压缩报文长度(重复内容建立字典),省去响应体
  • 合并上报:HTTP2多路复用,HTTP POST合并 参考:zhuanlan.zhihu.com/p/48121047

19.前端如何实现轮询

  • WebSocket通信协议,建立一个长链接,客户端监听服务端信息,此链接可手动关闭。
  • setInterval,不停调用接口
initList () {
  this.myInterval = window.setInterval(() => {
    setTimeout(() => {
      this.dosomething() //调用接口的方法
    }, 1)
  }, 5000);
},
  • 每次调用接口加一个时间戳,成功回调/resolve时比对时间戳,判断时间戳是不是最新的,是表明是最新的返回。
  • 递归timeout
// 由于默认封装的getajax方法没有轮询这种功能,所以我自己写了一个轮询。
var checkLoading = function (timer) {
    setTimeout(function () {
        $.ajax({
            type: 'post',
            dataType: "text",
            url: url,
            data: param,
            cache: false,
            async: true,
            timeout: 8000,
            beforeSend: function () {
                // 在html页面添加了一个<div id="warning"></div>
                $('#warning').text('正在处理,请稍等!');
            },
            success: function (rs) {
                // 数据加载出来后remove掉
                $('#warning').remove();

                var rt_data = eval('(' + rs + ')');
                // 具体操作...
            },
            error: checkLoading
        });
    }, timer || 1000)
}
//第一次等1秒,第一次轮
checkLoading(1000)
  • 服务器主动推送SSE

20.用户在浏览器输入网址之后,发生了什么?

大致过程如下:可自由拓展

  • DNS解析,找到IP地址
  • 根据IP地址,找到对应的服务器
  • 建立TCP连接(三次握手)
  • 连接建立后,发出HTTP请求
  • 服务器根据请求作出HTTP响应
  • 浏览器得到响应内容,进行解析与渲染,显示网页内容
  • 断开连接(四次挥手)