记一次大厂一面总结

343 阅读15分钟

疫情的影响还是很严重的,公司一月份工资没发,二月工资减半,三月上班开始裁员。我太难了😭。生活还得继续啊; 计划没有变化快,为之后打算,自己也是趁着有点底,再加上看了看掘金春招的面试文章,短期提升了下,查缺补漏。当然还有很多不足。不提公司名了,下次再说😊。看正文:

1. http怎么建立链接的

① 建立tcp/ip连接(三次握手)

② 链接成功之后发送http请求报文。(服务器成功地接受了客户端请求返回状态码200)

③ 响应报文返回给客户端

④ 关闭tcp链接(如果设置请求头Connection:keep-alive,客户端建立连接之后,第二次建立连接时仍然可以使用该tcp通道,但不是持久的链接,有限制)

以上是我的简单理解,详细的可以参看三元大神的文章 链接

2. tcp三次握手

  • TCP 的连接是采用3次握手,而断开连接是采用4次握手

第一次握手:客户端通过标志位发送SYN = j 给服务端,表面客户端想要建立连接,并进入SYN_SEND状态,等待服务器确认;

第二次握手:服务端收到SYN标志后返回ACK =(j+1) 应答标志给客户端,同时自己也发送一个SYN包(syn=k),表面可以建立连接,此时服务器进入SYN_RECV状态。

第三次握手:客户端收到服务端端SYN+ACK应答标志后回传ACK =(k+1)标志给服务端,说明自己已经收到了ACK标志,此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。

(最低限度保证客户端和服务端都能发送,都能接收。如果传输丢包情况是tcp协议中规则中处理方式了,不是http应用层的了)

客户端先向其TCP发出连接释放报文段,同时停止发送数据,主动关闭TCP连接。客户端把释放报文段首部的FIN置1,其序号seq=u

服务器收到连接释放报文段后即发出确认ack=u+1,而这个报文段自己的序号是v;客户端收到后处于等待状态,等待连接释放报文段。从A到B发送信息的连接就释放了,这时的TCP连接处于半关闭状态,即客户端没有数据要发送了,但服务器要发送数据的话,客户端仍要接收的。

如果服务器没有数据发送了,其应用进程就通知TCP释放连接。此时服务器B发出的连接释放报文段必须为FIN=1。如果服务器的序号为w(在半关闭状态服务器可能又发送了一些数据)。B还必须重复上次已发送过的确认号ack=u+1。这时服务器就进入了LAST-ACK(最后确认)状态,等待A的确认

客户端在收到服务端的连接释放报文段后,必须对此发出确认。确认报文段中把ACK置1,确认号ack=w+1,而自己的序号是seq=u+1。经过时间等待计时器设置的时间2MSL(MSL:最长报文段寿命)后,客户端关闭。如果2MSL时间丢包了,服务端会再发送请求关闭的

3. 缓存策略

  • 浏览器缓存分为强缓存和协商缓存,浏览器加载一个页面的简单流程如下:
  1. 浏览器先根据这个资源的http头信息来判断是否命中强缓存。如果命中则直接加在缓存中的资源,并不会将请求发送到服务器,状态码依然是200, 来源指向 from memory cachefrom disk cache

from memory cache: 缓存资源在内存中,浏览器(或页面标签)关闭后内存中的缓存就会被释放,重新打开页面取不到该缓存。如果命中强缓存,刷新页面,该资源会出现from memory cache,从内存中读取缓存,速度最快。

from disk cache: 缓存资源在硬盘中,浏览器(或页面标签)关闭后硬盘中的缓存不会消失,下次进入页面还能从硬盘中获取。

  1. 如果未命中强缓存,则浏览器会将资源加载请求发送到服务器。服务器来判断浏览器本地缓存是否失效(协商)。若可以使用,则服务器并不会返回资源信息(返回304),浏览器继续从缓存加载资源。
  2. 如果未命中协商缓存,则服务器会将完整的资源返回给浏览器,浏览器加载新资源,并更新缓存。
  • 常见强缓存expires, cache-control(优先级高)
  1. expires

缓存过期时间,用来指定资源到期的时间,是服务器端的具体的时间点。GMT时间格式,Expires: Fri, 17 Apr 2020 07:44:44 GMT。Expires是Web服务器响应消息头字段,在响应http请求时告诉浏览器在过期时间前浏览器可以直接从浏览器缓存取数据,而无需再次请求

  1. Cache-Control

Cache-Control是一个相对时间,例如Cache-Control:3600,代表着资源的有效期是3600秒。由于是相对时间,并且都是与客户端时间比较,所以服务器与客户端时间偏差也不会导致问题。

常见取值
max-age 指定一个时间长度,在这个时间段内缓存是有效的,单位是s。例如设置 Cache-Control:max-age=31536000
no-store 禁止缓存,每次请求都要向服务器重新获取数据。
no-cache 强制所有缓存了该响应的用户,在使用已缓存的数据前,发送带验证器的请求到服务器。不是字面意思上的不缓存。
public 表明响应可以被任何对象(发送请求的客户端、代理服务器等等)缓存。
private 表明响应只能被单个用户(可能是操作系统用户、浏览器用户)缓存,是非共享的,不能被代理服务器缓存。
  • 协商缓存
  1. Last-Modify/If-Modify-Since

浏览器第一次请求一个资源的时候,服务器返回的header中会加上Last-Modify,Last-modify是一个时间标识该资源的最后修改时间,例如Last-Modify: Thu,31 Dec 2037 23:59:59 GMT

当浏览器再次请求该资源时,发送的请求头中会包含If-Modify-Since,该值为缓存之前返回的Last-Modify。服务器收到If-Modify-Since后,根据资源的最后修改时间判断是否命中缓存。

如果命中缓存,则返回304,并且不会返回资源内容,并且不会返回Last-Modify。由于对比的服务端时间,所以客户端与服务端时间差距不会导致问题。但是有时候通过最后修改时间来判断资源是否修改还是不太准确(资源变化了最后修改时间也可以一致)。于是出现了ETag/If-None-Match

  1. ETag/If-None-Match

与Last-Modify/If-Modify-Since不同的是,Etag/If-None-Match返回的是一个校验码(ETag: entity tag)。ETag可以保证每一个资源是唯一的,资源变化都会导致ETag变化。ETag值的变更则说明资源状态已经被修改。服务器根据浏览器上发送的If-None-Match值来判断是否命中缓存。

生成标识(可以联合使用)
1. 文件的i-node编号,此i-node非彼iNode。是Linux/Unix用来识别文件的编号。是的,识别文件用的不是文件名。使用命令’ls –I’可以看到。
2. 文件最后修改时间
3. 文件大小

4. cookie 安全

  1. 保存的敏感信息进行加密

     大部分加密都用的网上第三方库,如md5。(但是如果被人拿到是不是也可以反编译拿到信息,感觉也不是绝对安全)
    
  2. 设置HttpOnly为true。

     该属性作用防止Cookie值被页面脚本读取,但不是绝对的禁止,还是有泄露的风险,谁让黑客**niubility**呢
    
  3. 设置Cookie的Secure为true。

     只有在https协议下访问的时候,浏览器才会发送该Cookie。但是https只能保证传输过程中的安全,无法保证在本地的安全,还是不安全😭
    
  4. 设置cookie有效期,不要过长,合适即可

     **什么时候算合适呢,应该根据业务需求来,是否需要经常改变**
    
  5. session和cookie同时使用。

     sessionId虽然放在cookie中,但是相对的session更安全,可以将相对重要的信息存入session
    

后来我从掘金看到了这篇文章

当浏览器全面禁用第三方Cookie

5. this指向

  • this的指向在函数定义的时候是确定不了的,只有函数执行的时候才能确定this到底指向谁,实际上this的最终指向的是那个调用它的对象

一般情况指向window

var test = 123
function fn() {
	console.log(this.test)
	console.log(this) => window
}
fn() => 123

如果把var 改为let, 结果为undefined,作用域嘛你懂得

对象中函数使用

var test = 'uncertainty'
let obj = {
	word: 'hello uncertainty',
	fn() {
		console.log(this.word) => hello uncertainty
		console.log(this.test) => undefined
		console.log(this) => {word: "hello uncertainty", fn: ƒ}
	}
}
obj.fn()

this只会指向它的上一级对象,不管这个对象中有没有this要的东西

构造函数中的this

function fn() {
	this.name = 'hello uncertainty'
}
let test = new fn()
console.log(test.name) => 'hello uncertainty'

注意

返回对象
function fn() {
	this.name = 'hello world'
	return {}
}
let test = new fn()
console.log(test.name) =》 undefined

返回函数
function fn() {
	this.name = 'hello world'
	return function(){}
}
let test = new fn()
console.log(test.name) => 竟然是空字符串 oh my god!有没老哥讲解下

返回数字或者字符串,实例对象
function fn() {
	this.name = 'hello world'
	return '' / 1 / null / undefined
}

let test = new fn()
console.log(test.name) => hello world

call / apply

let obj1={
    a:'uncertainty'
};
let obj2={
    a: 'world',
    fn:function(){
        console.log(this.a);
    }
}
obj2.fn.call(obj1); => uncertainty  //传参不同 

箭头函数

箭头函数中没有this, 不能实例化

let obj1={
    a: 'uncertainty',
    fn:function(){    
        setTimeout(()=>{console.log(this.a)}); => uncertainty
    }
};
obj1.fn(); 

let obj2={
    a: 'uncertainty',
    fn:function(){    
        setTimeout(function(){console.log(this.a)}); => undefined
    }
};
obj2.fn();

6. 防止xss攻击

Cross-Site Scripting(跨站脚本攻击)简称XSS,是一种代码注入攻击。攻击者通过在目标网站上注入恶意脚本,使之在用户的浏览器上运行。利用这些恶意脚本,攻击者可获取用户的敏感信息如Cookie、SessionID等,进而危害数据安全。 XSS的本质是:恶意代码未经过滤,与网站正常的代码混在一起;浏览器无法分辨哪些脚本是可信的,导致恶意脚本被执行。

咱们看下怎么注入的

  1. 来自用户的UGC信息 (User Generated Content-用户原创内容,如写博客,写影评,短视频)

  2. 来自第三方的链接 (网站引入第三方链接,别人代码里有恶意脚本,会导致自己网站的不安全)

  3. URL 参数

     http://xxx/search?k="><script>console.log(document.cookie);</script>,服务端会解析出请求参数k,得到
     "><script>console.log(document.cookie);</script>,拼接到HTML中返回给浏览器,拼接到浏览器中,会执行,泄露信息
    
  4. POST 参数

  5. Referer (请求头中,表示来自哪个网址。服务端接收判定,可能来自不可信的来源)

  6. Cookie (可能来自其他子域注入,如第三方的数据统计,广告推送在cookie中)

  7. <a href=""></a>, <img src=""/>, background-image:url(), 都可能导致问题

分类

  • 存储型XSS (恶意代码提交到后台数据库存储)
  • 反射型XSS (恶意url,引诱用户点击,提交信息)
  • DOM型XSS (插入dom执行,属于前端范围)

XSS攻击的预防

  • 转义 HTML (可以使用第三方转义库)

  • 在使用.innerHTML、.outerHTML要注意,这类插入到页面中浏览器不能区分是否是恶意脚本,而应尽量使用 .textContent、.setAttribute()这种
  • 尽量不要使用onClick="go('{{action}}')"这种拼接内联事件的写法。通过.addEventlistener()事件绑定会更安全
  • 避免拼接HTML, 像vue的v-html, 不是很安全
  • HTTP-only Cookie: 禁止 JavaScript 读取某些敏感 Cookie
  • 验证码:防止脚本冒充用户提交危险操作
  • 可以用扫描工具自己排查

以上内容也是我在网络上的查找总结,会有不足之处,请多指正。说实话我的理解也不深,还得加强学习。

7. token

相较于传统的cookie和sessionId的方式认证,token(令牌)显得灵活,随机性强,更安全些。拿着这个牌,就成蒙多了,想去哪就去哪

正常token的流程

  1. 用户先用用户名跟密码登录;
  2. 服务端收到请求,去验证用户名与密码;验证成功后,服务端会签发一个token,token中包含着加密的用户信息,把这个token发送给客户端;
  3. 客户端收到token以后可以把它存储起来,可以放在cookie里或者localStorage里;
  4. 客户端每次向服务端请求资源的时候需要带着存储的这个token,通常放在请求中;
  5. 服务端收到请求,先验证token,如果验证成功,就向客户端返回请求的数据。

我们公司的项目就是请求头增加Authorization字段,Bearer token形式

安全性

由于token的的随机性强,前后端自定义一些加密字段,黑客还是很难破解的(但觉得也不是绝对的,高手在民间啊😇)

  • token可以防止表单重复提交

      第一次提交,服务器端会生成token存储下来。第二次提交,服务器对比客户
      端发送过来的token和存储的token是否一致,如果一样,请求成功,更新
      token。若用户重复提交,第二次的验证判断将失败,因为用户提交的表单中
      的token 没变,但服务器端存储的token已经改变了。
    
  • 防止CSRF(跨站请求伪造)

      服务器端会对Token值进行验证,判断是否和session中的Token值相等,若相等
      ,则可以证明请求有效,不是伪造的(但是如果都是使用网上的生成
      token的第三方库,让黑客知道了算法规则,进而进行解密,是不是还会泄露信息?)
    

8. https区别

http和https是网站中常用的通信协议

  1. HTTP协议是一种使用明文数据传输的网络协议,用户信息存在安全隐患;HTTPS协议就是在HTTP的基础上增加了数据加密,数据是对称加密的

  1. HTTPS网站和HTTP网站还有一个很重要的区别,就是对搜索排名的提升

  2. HTTP协议默认端口80, HTTPS协议默认端口443

  3. HTTPS需要申请证书,有一点的费用

  4. HTTPS请求的时候会有个认证,时间会较http来说长一点

HTTPS 协议之所以是安全的是因为 HTTPS 协议会对传输的数据进行加密,而加密过程是使用了非对称加密实现。但其实:HTTPS 在内容传输的加密上使用的是对称加密,非对称加密只作用在证书验证阶段

① 证书验证阶段:

1)浏览器发起 HTTPS 请求;
2)服务端返回 HTTPS 证书;
3)客户端验证证书是否合法,如果不合法则提示告警。

② 数据传输阶段:

1)当证书验证合法后,在本地生成随机数;
2)通过公钥加密随机数,并把加密后的随机数传输到服务端;
3)服务端通过私钥对随机数进行解密;
4)服务端通过客户端传入的随机数构造对称加密算法,对返回结果内容进行加密后传输。

9. 手写bind

可能面试官看我比较菜,问了些简单的编程😂。(手写apply, call, bind大家可以在掘金搜索文章上查看)

Function.prototype.myBind = function(thisObj, ...args) {
    let self = this
    
    let result = function() {
        if (this instanceof self) {
            // new 实例的优先级高
            self.apply(this, [...args, ...arguments])
        } else {
            self.apply(thisObj, [...args, ...arguments])
        }
    }
    result.prototype = Object.create(self.prototype)
    return result
}
function fn() {
    console.log(this)
}
let obj = {
    name: 'hello world'
}
fn.bind(obj)()

let testFn = fn.bind()
let test = new testFn()
console.log(test) //this指向

10. 实现个链表 我理解的链表为:

function ListNode(x){
    this.val = x;
    this.next = null;
}

或者

class ListNode{
    constructor(val) {
        this.val = val
        this.next = null
    }
}

数组[1, 2, 3]生成上面形式的链表

11. 链表反转

如上,生成[3, 2, 1]形式的链表

以上是不准视频面试的经历,很感慨我这等货色也能得到个大厂面试机会,原来世间的爱不只有饭岛啊!平时会感觉自己有些不足,能干活,但是在一些深入方面缺失;这很好地诠释了屌丝和大佬的区别,不被蹂躏不能成长。经过这次面试虽然东西不多,但却是掌握不足,或者没有关注。但是也知道了优秀的人会和优秀的人在一起,或者跟着优秀的人耳濡目染,自己再加个油,也会得到提升。每个程序员都有个大厂梦,万一实现了呢!准备接下来的面试去了😁

文中的图片来源于网络,如有侵权麻烦告知,我会删除的。谢谢阅读!