前端面试题《跨域》

1.什么是跨域

协议、端口和域名不一致导致的跨域 跨域是因为浏览器需要遵守同源策略,发出的请求即使相应成功,也被浏览器拦截下来

2.同源策略

同源策略限制了从同一个源加载的文档或脚本如何与来自另一个源的资源进行交互、这是一个用于隔离潜在恶意文件的重要安全机制、

3.为什么有同源策略

如果缺少了同源策略,浏览器很容易受到XSS、CSFR等攻击。

1、 防御 XSS 攻击

XSS,即 Cross Site Script,中译是跨站脚本攻击。

HttpOnly 防止劫取 Cookie

用户的输入检查

服务端的输出检查

2、防御 CSRF 攻击

CSRF,即 Cross Site Request Forgery,中译是跨站请求伪造,是一种劫持受信任用户向服务器发送非预期请求的攻击方式。

验证码

Referer Check

Token验证

4.跨域的解决方案

1、通过jsonp跨域

2、document.domain + iframe跨域

3、location.hash + iframe

4、window.name + iframe跨域

5、postMessage跨域

6、跨域资源共享(CORS)

7、nginx代理跨域

8、nodejs中间代理跨域

9、WebSocket协议跨域

5.jsonp原理

jsonp的核心则是动态添加 script 标签调用服务器提供的js脚本,允许用户传递一个callback参数给服务器,然后服务器返回数据时会将这个callback参数作为函数名老包裹JSON数据,这样客户端就可以随意定制自己的函数来自动处理返回数据了

JSONP 使用简单且兼容性不错,但是只限于 get 请求。(缺点)

通用的办法如下:

<script> var script = document.createElement(script); script.type = text/javascript; // 传参一个回调函数名给后端,方便后端返回时执行这个在前端定义的回调函数 script.src = :8080/login? user=admin&callback=handleCallback; document.head.appendChild(script); // 回调执行函数 function handleCallback(value) { console.log(value) } </script>

vue.js实现

this.$http.jsonp(:8080/login, { params: {}, jsonp: handleCallback }).then((res) => { console.log(res); })

后端node.js代码实例

var querystring = require(querystring); var http = require(http); var server = http.createServer(); server.on(request, function(req, res) { var params = qs.parse(req.url.split(?)[1]); var fn = params.callback; // jsonp返回设置 res.writeHead(200, { Content-Type: text/javascript }); res.write(fn + ( + JSON.stringify(params) + )); res.end(); }); server.listen(8080); console.log(Server is running at port 8080...);

6.常见的跨域场景

7.postMessage跨域

这是由H5提出来的的API,IE8以上支持这个功能。

window.postMessage() 方法可以安全地实现跨源通信。

window.postMessage() 方法被调用时,会在所有页面脚本执行完毕之后,向目标窗口派发一个 MessageEvent 消息。

用法:postMessage(data,origin)方法接受两个参数

data: html5规范支持任意基本类型或可复制的对象,但部分浏览器只支持字符串,所以传参时最好用JSON.stringify()序列化。

origin: 协议+主机+端口号,也可以设置为"*",表示可以传递给任意窗口,如果要指定和当前窗口同源的话设置为"/"。

例子:

1.)a.html:(/a.html)

<iframe id="iframe" src="/b.html" style="display:none;"></iframe> <script> var iframe = document.getElementById(iframe); iframe.onload = function() { var data = { name: aym }; // 向domain2传送跨域数据 iframe.contentWindow.postMessage(JSON.stringify(data), ); }; // 接受domain2返回数据 window.addEventListener(message, function(e) { alert(data from domain2 ---> + e.data); }, false); </script> 78910111213141516

2.)b.html:(/b.html)

<script> // 接收domain1的数据 window.addEventListener(message, function(e) { alert(data from domain1 ---> + e.data); var data = JSON.parse(e.data); if (data) { data.number = 16; // 处理后再发回domain1 window.parent.postMessage(JSON.stringify(data), ); } }, false); </script>

8.资源共享跨域(CORS)

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

目前,所有浏览器都支持该功能(IE8+:IE8/9需要使用XDomainRequest对象来支持CORS)),CORS也已经成为主流的跨域解决方案。

但不能实现修改cookie中domain信息,不方便当前域cookie写入,即不能实现登录认证。

9.nginx代理跨域

实现思路:通过nginx配置一个代理服务器(域名与domain1相同,端口不同)做跳板机,反向代理访问domain2接口,并且可以顺便修改cookie中domain信息,方便当前域cookie写入,实现跨域登录。

nginx具体配置:

#proxy服务器 server { listen 81; server_name www.domain1.com; location / { proxy_pass :8080; #反向代理 proxy_cookie_domain www.domain2.com www.domain1.com; #修改cooki里域名 index index.html index.htm; # 当用webpack-dev-server等中间件代理接口访问nignx时,此时无浏览器参与, 故没有同源限制,下面的跨域配置可不启用 add_header Access-Control-Allow-Origin ; #当前端只跨域不带cookie时,可为* add_header Access-Control-Allow-Credentials true; } }

10.nodejs中间件代理跨域

node中间件实现跨域代理,原理大致与nginx相同,都是通过启一个代理服务器,实现数据的转发,也可以通过设置cookieDomainRewrite参数修改响应头中cookie中域名,实现当前域的cookie写入,方便接口登录认证。

1、 非vue框架的跨域(2次跨域)

利用node + express + http-proxy-middleware搭建一个proxy服务器。

中间件服务器:

var express = require(express); var proxy = require(http-proxy-middleware); var app = express(); app.use(/, proxy({ // 代理跨域目标接口 target: :8080, changeOrigin: true, // 修改响应头信息,实现跨域并允许带cookie onProxyRes: function(proxyRes, req, res) { res.header(Access-Control-Allow-Origin,); res.header(Access-Control-Allow-Credentials, true); } // 修改响应信息中的cookie域名 cookieDomainRewrite: www.domain1.com // 可以为false,表示不修改 })); app.listen(3000); console.log(Proxy server is listen at port 3000...); 789101112131415161718192021

2、 vue框架跨域

利用node + webpack + webpack-dev-server代理接口跨域。在开发环境下,由于vue渲染服务和接口代理服务都是webpack-dev-server同一个,所以页面与代理接口之间不再跨域,无须设置headers跨域信息了。

webpack.config.js部分配置: module.exports = { entry: {}, module: {}, ... devServer: { historyApiFallback: true, proxy: [{ context: /sbcweb, target: :8080, // 代理跨域目标接口 changeOrigin: true, secure: false, // 当代理某些https服务报错时用 cookieDomainRewrite: www.domain1.com // 可以为false,表示不修改 // 填写域名表示可以更改cookie的域名值为此域名,便可以导入cookie到网站中。 }], noInfo: true } }

11.webscoket协议跨域

WebSocket protocol是HTML5一种新的协议。它实现了浏览器与服务器全双工通信,同时允许跨域通讯,是server push技术的一种很好的实现。

原生WebSocket API使用起来不太方便,我们使用Socket.io,它很好地封装了webSocket接口,提供了更简单、灵活的接口,也对不支持webSocket的浏览器提供了向下兼容。

WebSocket不会专门用来做跨域,而是作为消息推送或者聊天等.