Java高级架构师面试分享:阿里P6、滴滴两年工作经验成功面试总结

为了感谢支持我的朋友!整理了一份Java高级架构资料、Spring源码分析、Dubbo、Redis、Netty、zookeeper、Spring cloud、分布式等资。关注后点这里直达即可获取!

image.png

滴滴(一面)

你知道哪些安全问题,如何避免

小提示:这里我简单讲解了一下Vue中的v-html防范XSS攻击。

XSS(跨站脚本攻击)

XSS,即 Cross Site Script,中译是跨站脚本攻击;其原本缩写是 CSS,但为了和层叠样式表(Cascading Style Sheet)有所区分,因而在安全领域叫做 XSS。

XSS 攻击是指攻击者在网站上注入恶意的客户端代码,通过恶意脚本对客户端网页进行篡改,从而在用户浏览网页时,对用户浏览器进行控制或者获取用户隐私数据的一种攻击方式。

攻击者对客户端网页注入的恶意脚本一般包括 JavaScript,有时也会包含 HTML 和 Flash。有很多种方式进行 XSS 攻击,但它们的共同点为:将一些隐私数据像 cookie、session 发送给攻击者,将受害者重定向到一个由攻击者控制的网站,在受害者的机器上进行一些恶意操作。

XSS攻击可以分为3类:反射型(非持久型)、存储型(持久型)、基于DOM。

反射型

反射型 XSS 只是简单地把用户输入的数据 “反射” 给浏览器,这种攻击方式往往需要攻击者诱使用户点击一个恶意链接(攻击者可以将恶意链接直接发送给受信任用户,发送的方式有很多种,比如 email, 网站的私信、评论等,攻击者可以购买存在漏洞网站的广告,将恶意链接插入在广告的链接中),或者提交一个表单,或者进入一个恶意网站时,注入脚本进入被攻击者的网站。最简单的示例是访问一个链接,服务端返回一个可执行脚本:

<pre style="box-sizing: border-box; font-family: monospace; font-size: 1em; margin: 20px 0px; padding: 15px; white-space: pre; border: 0px; background-color: rgb(244, 245, 246); word-break: break-all;">const http = require('http');
function handleReequest(req, res) {
 res.setHeader('Access-Control-Allow-Origin', '*');
 res.writeHead(200, {'Content-Type': 'text/html; charset=UTF-8'});
 res.write('<script>alert("反射型 XSS 攻击")</script>');
 res.end();
}
const server = new http.Server();
server.listen(8001, '127.0.0.1');
server.on('request', handleReequest);

存储型

存储型 XSS 会把用户输入的数据 “存储” 在服务器端,当浏览器请求数据时,脚本从服务器上传回并执行。这种 XSS 攻击具有很强的稳定性。比较常见的一个场景是攻击者在社区或论坛上写下一篇包含恶意 JavaScript 代码的文章或评论,文章或评论发表后,所有访问该文章或评论的用户,都会在他们的浏览器中执行这段恶意的 JavaScript 代码:

<pre style="box-sizing: border-box; font-family: monospace; font-size: 1em; margin: 20px 0px; padding: 15px; white-space: pre; border: 0px; background-color: rgb(244, 245, 246); word-break: break-all;">// 例如在评论中输入以下留言
// 如果请求这段留言的时候服务端不做转义处理,请求之后页面会执行这段恶意代码
<script>alert('xss 攻击')</script>

基于DOM

基于 DOM 的 XSS 攻击是指通过恶意脚本修改页面的 DOM 结构,是纯粹发生在客户端的攻击:

<pre style="box-sizing: border-box; font-family: monospace; font-size: 1em; margin: 20px 0px; padding: 15px; white-space: pre; border: 0px; background-color: rgb(244, 245, 246); word-break: break-all;"><h2>XSS: </h2>
<input type="text" id="input">
<button id="btn">Submit</button>
<div id="div"></div>
<script>
 const input = document.getElementById('input');
 const btn = document.getElementById('btn');
 const div = document.getElementById('div');
 let val;

 input.addEventListener('change', (e) => {
 val = e.target.value;
 }, false);
 btn.addEventListener('click', () => {
 div.innerHTML = `<a href=${val}>testLink</a>`
 }, false);
</script>

点击 Submit 按钮后,会在当前页面插入一个链接,其地址为用户的输入内容。如果用户在输入时构造了如下内容:

<pre style="box-sizing: border-box; font-family: monospace; font-size: 1em; margin: 20px 0px; padding: 15px; white-space: pre; border: 0px; background-color: rgb(244, 245, 246); word-break: break-all;">'' onclick=alert(/xss/)

用户提交之后,页面代码就变成了:

<pre style="box-sizing: border-box; font-family: monospace; font-size: 1em; margin: 20px 0px; padding: 15px; white-space: pre; border: 0px; background-color: rgb(244, 245, 246); word-break: break-all;"><a href onlick="alert(/xss/)">testLink</a>

此时,用户点击生成的链接,就会执行对应的脚本。

XSS攻击防范

HttpOnly 防止劫取 Cookie:HttpOnly 最早由微软提出,至今已经成为一个标准。浏览器将禁止页面的Javascript 访问带有 HttpOnly 属性的Cookie。上文有说到,攻击者可以通过注入恶意脚本获取用户的 Cookie 信息。通常 Cookie 中都包含了用户的登录凭证信息,攻击者在获取到 Cookie 之后,则可以发起 Cookie 劫持攻击。所以,严格来说,HttpOnly 并非阻止 XSS 攻击,而是能阻止 XSS 攻击后的 Cookie 劫持攻击。

输入检查:不要相信用户的任何输入。 对于用户的任何输入要进行检查、过滤和转义。建立可信任的字符和 HTML 标签白名单,对于不在白名单之列的字符或者标签进行过滤或编码。在 XSS 防御中,输入检查一般是检查用户输入的数据中是否包含 <,> 等特殊字符,如果存在,则对特殊字符进行过滤或编码,这种方式也称为 XSS Filter。而在一些前端框架中,都会有一份 decodingMap, 用于对用户输入所包含的特殊字符或标签进行编码或过滤,如 <,>,script,防止 XSS 攻击:

<pre style="box-sizing: border-box; font-family: monospace; font-size: 1em; margin: 20px 0px; padding: 15px; white-space: pre; border: 0px; background-color: rgb(244, 245, 246); word-break: break-all;">// vuejs 中的 decodingMap
// 在 vuejs 中,如果输入带 script 标签的内容,会直接过滤掉
const decodingMap = {
 '<': '<',
 '>': '>',
 '"': '"',
 '&': '&',
 '
': '\n'
}

输出检查:用户的输入会存在问题,服务端的输出也会存在问题。一般来说,除富文本的输出外,在变量输出到 HTML 页面时,可以使用编码或转义的方式来防御 XSS 攻击。例如利用 sanitize-html 对输出内容进行有规则的过滤之后再输出到页面中。

CSRF/XSRF(跨站请求伪造)

CSRF,即 Cross Site Request Forgery,中译是跨站请求伪造,是一种劫持受信任用户向服务器发送非预期请求的攻击方式。通常情况下,CSRF 攻击是攻击者借助受害者的 Cookie 骗取服务器的信任,可以在受害者毫不知情的情况下以受害者名义伪造请求发送给受攻击服务器,从而在并未授权的情况下执行在权限保护之下的操作。

Cookie

Cookie 是服务器发送到用户浏览器并保存在本地的一小块数据,它会在浏览器下次向同一服务器再发起请求时被携带并发送到服务器上。Cookie 主要用于以下三个方面:

  • 会话状态管理(如用户登录状态、购物车、游戏分数或其它需要记录的信息)
  • 个性化设置(如用户自定义设置、主题等)
  • 浏览器行为跟踪(如跟踪分析用户行为等)

而浏览器所持有的 Cookie 分为两种:

  • Session Cookie(会话期 Cookie):会话期 Cookie 是最简单的Cookie,它不需要指定过期时间(Expires)或者有效期(Max-Age),它仅在会话期内有效,浏览器关闭之后它会被自动删除。
  • Permanent Cookie(持久性 Cookie):与会话期 Cookie 不同的是,持久性 Cookie 可以指定一个特定的过期时间(Expires)或有效期(Max-Age)。
<pre style="box-sizing: border-box; font-family: monospace; font-size: 1em; margin: 20px 0px; padding: 15px; white-space: pre; border: 0px; background-color: rgb(244, 245, 246); word-break: break-all;">res.setHeader('Set-Cookie', ['mycookie=222', 'test=3333; expires=Sat, 21 Jul 2018 00:00:00 GMT;']);

上述代码创建了两个 Cookie:mycookie 和 test,前者属于会话期 Cookie,后者则属于持久性 Cookie。

CSRF攻击

使登录用户访问攻击者的网站,发起一个请求,由于 Cookie 中包含了用户的认证信息,当用户访问攻击者准备的攻击环境时,攻击者就可以对服务器发起 CSRF 攻击。

在这个攻击过程中,攻击者借助受害者的 Cookie 骗取服务器的信任,但并不能拿到 Cookie,也看不到 Cookie 的内容。而对于服务器返回的结果,由于浏览器同源策略的限制,攻击者也无法进行解析。(攻击者的网站虽然是跨域的,但是他构造的链接是源网站的,跟源网站是同源的,所以能够携带cookie发起访问)。

但是攻击者无法从返回的结果中得到任何东西,他所能做的就是给服务器发送请求,以执行请求中所描述的命令,在服务器端直接改变数据的值,而非窃取服务器中的数据。例如删除数据、修改数据,新增数据等,无法获取数据。

CSRF攻击防范

验证码:验证码被认为是对抗 CSRF 攻击最简洁而有效的防御方法。从上述示例中可以看出,CSRF 攻击往往是在用户不知情的情况下构造了网络请求。而验证码会强制用户必须与应用进行交互,才能完成最终请求。因为通常情况下,验证码能够很好地遏制 CSRF 攻击。但验证码并不是万能的,因为出于用户考虑,不能给网站所有的操作都加上验证码。因此,验证码只能作为防御 CSRF 的一种辅助手段,而不能作为最主要的解决方案。

Referer Check:根据 HTTP 协议,在 HTTP 头中有一个字段叫 Referer,它记录了该 HTTP 请求的来源地址。通过 Referer Check,可以检查请求是否来自合法的"源"。

添加token验证:要抵御 CSRF,关键在于在请求中放入攻击者所不能伪造的信息,并且该信息不存在于 Cookie 之中。可以在 HTTP 请求中以参数的形式加入一个随机产生的 token,并在服务器端建立一个拦截器来验证这个 token,如果请求中没有 token 或者 token 内容不正确,则认为可能是 CSRF 攻击而拒绝该请求。

介绍一下Graphql

小提示:这道题是给自己挖了一个坑,抱着学习的心态尝试使用Graphql技术,却没有好好理解是在什么场景下为了解决什么问题才应该使用,也没有好好准备如何描述新技术,往往这种不熟悉的技术自己在简历中应该留存一些心眼,尽量不要提,否则答不上来会很尴尬,让面试官怀疑你的项目成分。

什么是Graphql

GraphQL是一种API查询语言。API接口的返回值可以从静态变为动态,即调用者来声明接口返回什么数据,可以进一步解耦前后端。在Graphal中,预先定义Schema和声明Type来达到动态获取接口数据的目的:

  • 对于数据模型的抽象是通过Type来描述的
  • 对于接口获取数据的逻辑是通过Schema来描述的

为什么要使用Graphql:

  • 接口数量众多维护成本高
  • 接口扩展成本高
  • 接口响应的数据格式无法预知
  • 减少无用数据的请求, 按需获取
  • 强类型约束(API的数据格式让前端来定义,而不是后端定义)

Type(数据模型的抽象)

Type简单可以分为两种,一种叫做Scalar Type(标量类型),另一种叫做Object Type(对象类型):

  • Scalar Type(标量类型):内建的标量包含,String、Int、Float、Boolean、Enum
  • Object Type(对象类型):感觉类似于TypeScript的接口类型
  • Type Modifier(类型修饰符):用于表明是否必填等

Schema(模式)

定义了字段的类型、数据的结构,描述了接口数据请求的规则

Query(查询、操作类型)

查询类型: query(查询)、mutation(更改)和subscription(订阅)

  • query(查询):当获取数据时,应当选取Query类型
  • mutation(更改):当尝试修改数据时,应当使用mutation类型
  • subscription(订阅):当希望数据更改时,可以进行消息推送,使用subscription类型

Resolver(解析函数)

提供相关Query所返回数据的逻辑。Query和与之对应的Resolver是同名的,这样在GraphQL才能把它们对应起来。解析的过程可能是递归的,只要遇到非标量类型,会尝试继续解析,如果遇到标量类型,那么解析完成,这个过程叫做解析链。

说说Vue中$nextTick的实现原理

小提示:如果面试者使用的是Vue技术栈,那么 n e x t T i c k 的 原 理 是 一 个 高 频 问 题 , 面 试 者 借 此 可 以 追 问 的 东 西 较 多 , 例 如 浏 览 器 的 E v e n t L o o p 、 微 任 务 和 宏 任 务 、 N o d e . j s 的 E v e n t L o o p 、 异 步 更 新 D O M ( 响 应 式 的 数 据 f o r 循 环 改 变 了 1000 次 为 什 么 视 图 只 更 新 了 一 次 ) 、 nextTick的原理是一个高频问题,面试者借此可以追问的东西较多,例如浏览器的Event Loop、微任务和宏任务、Node.js的Event Loop、异步更新DOM(响应式的数据for循环改变了1000次为什么视图只更新了一次)、 nextTick西EventLoopNode.jsEventLoopDOMfor1000nextTick历史版本问题等等。

这个如果不是很清楚的具体可查看我之前写的文章你真的理解$nextTick么。

Vue响应式原理

谈谈对闭包的理解

JSONP的实现原理

CSS中的BFC

如何实现居中

水平居中

  • 若是行内元素,给其父元素设置text-align:center即可实现行内元素水平居中
  • 若是块级元素,该元素设置margin:0 auto即可(元素需要定宽)
  • 若是块级元素,设置父元素为flex布局,子元素设置margin:0 auto即可(子元素不需要定宽)
  • 使用flex 2012年版本布局,可以轻松的实现水平居中,子元素设置如下:
// flex容器
```

 
 
// flex项目
.box { width: 200px; height: 200px; display: flex; // 使内部的flex项目水平居中 justify-content: center; background-color: pink; } /* .box-center { width: 50%; background-color: greenyellow; } */ ``` * 使用绝对定位和CSS3新增的属性transform(这个属性还和GPU硬件加速、固定定位相关) ```
.box {
 width: 200px;
 height: 200px;
 position: relative;
 background-color: pink;
}
.box-center {
 position: absolute;
 left:50%;
 // width: 50%;
 height: 100%;
 // 通过 translate() 方法,元素从其当前位置移动,根据给定的 left(x 坐标) 和 top(y 坐标) 位置参数:
 // translate(x,y)	定义 2D 转换。
 // translateX(x)	定义转换,只是用 X 轴的值。
 // translateY(y)	定义转换,只是用 Y 轴的值。
 // left: 50% 先整体向父容器的左侧偏移50%,此时是不能居中的,因为元素本身有大小
 // 接着使用transform使用百分比向左偏移本身的宽度的一半实现水平居中(这里的百分比以元素本身的宽高为基准)
 transform:translate(-50%,0);
 background-color: greenyellow;
}
```

*   使用绝对定位和margin-left(元素定宽)
```
.box {
 width: 200px;
 height: 200px;
 position: relative;
 background-color: pink;
}
.box-center {
 position: absolute;
 left:50%;
 height: 100%;
 // 类似于transform
 // width: 50%;
 // margin-left: -25%;
 width: 100px;
 margin-left: -50px;
 background-color: greenyellow;
}
```

垂直居中

*   若元素是单行文本, 则可设置line-height等于父元素高度
*   若是块级元素,设置父元素为flex布局,子元素设置margin: auto 0即可(子元素不需要定宽)
*   若元素是行内块级元素,基本思想是使用display: inline-block, vertical-align: middle和一个伪元素让内容块处于容器中央:
```
.box {
 height: 100px;
}
.box::after, .box-center{
 display:inline-block;
 vertical-align:middle;
}
.box::after{
 content:'';
 height:100%;
}
```

居中元素高度不定

*   可用 vertical-align 属性(vertical-align只有在父层为 td 或者 th 时才会生效,,对于其他块级元素,例如 div、p 等,默认情况是不支持的),为了使用vertical-align,我们需要设置父元素display:table, 子元素 display:table-cell;vertical-align:middle:
```
.box {
 height: 100px;
 display: table;
}
 .box-center{
 display: table-cell;
 vertical-align:middle;
}
```

*   可用 Flex 2012版, 这是CSS布局未来的趋势。Flexbox是CSS3新增属性,设计初衷是为了解决像垂直居中这样的常见布局问题:
```
.box {
 height: 100px;
 display: flex;
 align-items: center;
}
```

优点:内容块的宽高任意, 优雅的溢出. 可用于更复杂高级的布局技术中. 缺点:IE8/IE9不支持、需要浏览器厂商前缀、渲染上可能会有一些问题。

*   可用 transform ,设置父元素相对定位:
```
.box {
 height: 100px;
 position: relative;
 background-color: pink;
}
.box-center {
 position: absolute;
 top: 50%;
 transform: translate(0, -50%);
 background-color: greenyellow;
}
```

缺点:IE8不支持, 属性需要追加浏览器厂商前缀,可能干扰其他 transform 效果,某些情形下会出现文本或元素边界渲染模糊的现象。

居中元素高度固定

*   设置父元素相对定位,子元素如下css样式:
```
.box {
 position:relative;
 height: 100px;
 background-color: pink;
}
.box-center{
 position:absolute;
 top:50%;
 // 注意不能使用百分比
 // margin的百分比计算是相对于父容器的width来计算的,甚至包括margin-top和margin-bottom
 height: 50px;
 margin-top: -25px;
}
```

*   设置父元素相对定位, 子元素如下css样式:
```
.box {
 position:relative;
 width: 200px;
 height: 200px;
 background-color: pink;
}
.box-center{
 position:absolute;
 top: 0;
 bottom: 0;
 margin: auto 0;
 height: 100px;
 background-color: greenyellow;
}
```

水平垂直居中

*   Flex布局(子元素是块级元素)
```
.box {
 display: flex;
 width: 100px;
 height: 100px;
 background-color: pink;
}
.box-center{
 margin: auto;
 background-color: greenyellow;
}
```

*   Flex布局
```
.box {
 display: flex;
 width: 100px;
 height: 100px;
 background-color: pink;
 justify-content: center;
 align-items: center;
}
.box-center{
 background-color: greenyellow;
}
```

*   绝对定位实现(定位元素定宽定高)
```
.box {
 position: relative;
 height: 100px;
 width: 100px;
 background-color: pink;
}
.box-center{
 position: absolute;
 left: 0;
 right: 0;
 bottom: 0;
 top: 0;
 margin: auto;
 width: 50px;
 height: 50px;
 background-color: greenyellow;
}
复制代码
```

用过Flex么,能简单介绍一下么

> 小提示:如果在项目中使用过,可简单介绍一下自己使用Flex解决过什么问题,这里我在项目中印象比较深刻的是使用Flex解决上面内容高度不固定,下面内容高度自动撑满父容器剩余高度的问题。

如果不是很清楚Flex,可以查看阮一峰的文章Flex 布局教程:语法篇。面试官追问,那么除了Flex,你还知道Grid么?这个由于兼容性问题,我一直没有好好研究过,这里可查看阮一峰的文章CSS Grid网格布局教程。

bind的源码实现

> 小提示:这里我回答使用函数柯里化加上apply或者call可实现bind,面试官追问了一些具体的实现细节。

后来我自己粗糙的实现了一下,仅供参考:
```
Function.prototype.myCall = function (obj) {
 let context = obj || window
 obj.fn = this
 let args = [...arguments].splice(1)
 let result = obj.fn(...args)
 delete obj.fn
 return result
}
Function.prototype.myApply = function (obj) {
 let context = obj || window
 obj.fn = this
 let args = arguments[1]
 let result
 if (args) {
 result = obj.fn(...args)
 } else {
 result = obj.fn()
 }
 delete obj.fn
 return result
}
Function.prototype.myBind = function (obj) {
 let context = obj || window
 let _this = this
 let _args = [...arguments].splice(1)
 return function () {
 let args = arguments
 // 产生副作用
 // return obj.fn(..._args, ...args)
 return _this.apply(context, [..._args, ...args])
 }
}
function myFun (argumentA, argumentB) {
 console.log(this.value)
 console.log(argumentA)
 console.log(argumentB)
 return this.value
}
let obj = {
 value: 'ziyi2'
}
console.log(myFun.myCall(obj, 11, 22))
console.log(myFun.myApply(obj, [11, 22]))
console.log(myFun.myBind(obj, 33)(11, 22))
```

伪类和伪元素的区别

> 小提示:这个问题我当时懵了一下,一下子没反应过来面试官想要问什么,就答了这两者在CSS优先级上有区别,然后由于遇到不会的问题有些紧张就多说了一些废话,但显然这不是面试官想要的答案并且消耗了面试官面试的耐心,说他问的不是这个。这里再次提示大家,如果你感觉你说不清楚,但是你又知道一点,我建议你说不知道,不要纠结,面试官不会因为你不知道一个问题就PASS你,相反你说了一些无关紧要的废话,反而在消耗面试官的耐性,增加负面印象。

伪类和伪元素是用来修饰不在文档树中的部分,比如,一句话中的第一个字母,或者是列表中的第一个元素。下面分别对伪类和伪元素进行解释:

伪类用于当已有元素处于的某个状态时,为其添加对应的样式,这个状态是根据用户行为而动态变化的。比如说,当用户悬停在指定的元素时,我们可以通过:hover来描述这个元素的状态。虽然它和普通的css类相似,可以为已有的元素添加样式,但是它只有处于dom树无法描述的状态下才能为元素添加样式,所以将其称为伪类。

伪元素用于创建一些不在文档树中的元素,并为其添加样式。比如说,我们可以通过:before来在一个元素前增加一些文本,并为这些文本添加样式。虽然用户可以看到这些文本,但是这些文本实际上不在文档树中。

区别

伪类的操作对象是文档树中已有的元素,而伪元素则创建了一个文档树外的元素。因此,伪类与伪元素的区别在于:**有没有创建一个文档树之外的元素。**

CSS3规范中的要求使用双冒号(::)表示伪元素,以此来区分伪元素和伪类,比如::before和::after等伪元素使用双冒号(::),:hover和:active等伪类使用单冒号(:)。除了一些低于IE8版本的浏览器外,大部分浏览器都支持伪元素的双冒号(::)表示方法。

小结

对于滴滴的这次面试,我感觉到自己准备的不是很充分,尤其是自己简历上的项目技术Graphql。同时对于自己不会的题目强行做了一些解释说明,其实应该简洁明了的告诉面试官不会。

## 关注公众号领资料

搜索公众号【程序员黑哥】,回复【666】,即可获取大量优质电子书和一份Java高级架构资料、Spring源码分析、Dubbo、Redis、Netty、zookeeper、Spring cloud、分布式等视频资料



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值