老生常谈的前端高频面试题 您所在的位置:网站首页 浏览器会阻止跨域请求 老生常谈的前端高频面试题

老生常谈的前端高频面试题

2024-06-29 11:30| 来源: 网络整理| 查看: 265

在这里插入图片描述

浏览器的跨域问题

为了防止网站遭到恶意攻击,导致信息被窃取,所以浏览器设计了同源策略。

为什么浏览器会禁止跨域

浏览器是很开放的,只要在地址栏里面输入网址或者点击某个链接就可以访问了。正是因为这种开放的形态,才需要对浏览器做出限制,保护用户的信息安全。因此同源策略就是用来保护信息安全的。

什么是同源策略

同源政策由 Netscape 公司引入浏览器。同源策略是一个安全策略。所谓的同源,指的是协议,域名,端口相同。

浏览器出于安全方面的考虑,只允许本域名下的接口交互,不同源的客户端脚本,在没有明确授权的情况下,不能读写对方的资源。

Cookie、LocalStorage 和 IndexDB 无法读取DOM 和 JS 对象无法获取 如何解决跨域问题 JSONP JSONP 实现跨域的原理

浏览器的同源策略限制不允许跨域请求;但页面中的 script、img、iframe标签是例外不受同源策略限制。

Jsonp 就是利用script标签跨域特性进行请求,通过 标签指向一个需要访问的地址并提供一个回调函数来接收数据。

思路:客户端事先准备一个接收数据的全局函数,之后客户端解析 script 标签发出请求。服务端接受到请求之后,返回函数的调用。客户端接收数据,执行回调。

假设JSONP请求如下:

jsonp({ url: 'http://path/to/server/b', params: {A: a, B: b}, success: function myCallback (response) {} })

背后其实在进行:

拼接一个script标签, ,从而触发对指定地址的GET请求

服务器端对这个GET请求进行处理,并返回字符串 "myCallback('response value')",前端script加载完之后,其实就是在script中执行 myCallback('response value'),就完成了跨域的请求,因此就是只能用GET。

JSONP 只能发 GET 请求,因为本质上 script 加载资源就是 GET

优缺点

优点:兼容性好,可以解决主流浏览器的跨域数据访问的问题。

缺点:只能进行 GET 请求,具有局限性,不安全。

function jsonp(data) { console.log(data) } const jsonp = ({ url, params, callbackName }) => { const generateUrl = () => { let dataSrc = '' for (let key in params) { if (params.hasOwnProperty(key)) { dataSrc += `${key}=${params[key]}&` } } dataSrc += `callback=${callbackName}` return `${url}?${dataSrc}` } return new Promise((resolve, reject) => { const scriptEle = document.createElement('script') scriptEle.src = generateUrl() document.body.appendChild(scriptEle) window[callbackName] = data => { resolve(data) document.removeChild(scriptEle) } }) }

发送 POST 请求,使用 iframe

const requestPost = ({url, data}) => { // 首先创建一个用来发送数据的iframe. const iframe = document.createElement('iframe') iframe.name = 'iframePost' iframe.style.display = 'none' document.body.appendChild(iframe) const form = document.createElement('form') const node = document.createElement('input') // 注册iframe的load事件处理程序,如果你需要在响应返回时执行一些操作的话. iframe.addEventListener('load', function () { console.log('post success') }) form.action = url // 在指定的iframe中执行form form.target = iframe.name form.method = 'post' for (let name in data) { node.name = name node.value = data[name].toString() form.appendChild(node.cloneNode()) } // 表单元素需要添加到主文档中. form.style.display = 'none' document.body.appendChild(form) form.submit() // 表单提交后,就可以删除这个表单,不影响下次的数据发送. document.body.removeChild(form) } // 使用方式 requestPost({ url: 'http://localhost:9871/api/iframePost', data: { msg: 'helloIframePost' } }) CORS 跨域资源共享

CORS 是 “跨域资源共享” (Cross-origin resource sharing ) 的缩写,跨域资源共享(CORS)是一种机制,是 W3C 标准。它允许浏览器向跨源服务器,发出XMLHttpRequest或Fetch请求。并且整个CORS通信过程都是浏览器自动完成的,不需要用户参与。解决 AJAX 只能同源通信的限制问题。

前提是,浏览器必须支持这个功能,并且服务端也必须同意这个跨域请求。

CORS通信与AJAX没有任何差别。只不过,浏览器会在请求中携带一些头信息,需要以此判断是否运行其跨域,然后在响应头中加入一些信息即可。

设计思想

使用自定义 HTTP 头部让浏览器与服务器进行沟通,从而决定请求或响应是成功还是失败。

两种请求方式

简单请求: 浏览器直接发出 CORS 请求。具体来说,在头信息之中,增加一个Origin字段。

(1)请求方法是以下三种方法之一:

HEADGETPOST

(2)HTTP的头信息不超出以下几种字段:

AcceptAccept-LanguageContent-LanguageLast-Event-IDContent-Type:只限于三个值 application/x-www-form-urlencoded、multipart/form-data、text/plain

当浏览器发现发现的ajax请求是简单请求时,会在请求头中携带一个字段:Origin。Origin中会指出当前请求属于哪个域(协议+域名+端口)。服务会根据这个值决定是否允许其跨域。

如果服务器允许跨域,需要在返回的响应头中携带下面信息:

Access-Control-Allow-Origin: http://manage.leyou.com Access-Control-Allow-Credentials: true

Access-Control-Allow-Origin:可接受的域,是一个具体域名或者*,代表任意 Access-Control-Allow-Credentials:是否允许携带cookie,默认情况下,cors不会携带cookie,除非这个值是true

注意:如果跨域请求要想操作cookie,需要满足3个条件:

服务的响应头中需要携带Access-Control-Allow-Credentials并且为true。浏览器发起ajax需要指定withCredentials 为true响应头中的Access-Control-Allow-Origin一定不能为*,必须是指定的域名。 高级请求: 必须首先使用 OPTIONS方法发起一个预检请求到服务器,以获知服务器是否允许该实际请求。服务器对 AJAX 跨域请求设置限制条件。不符合简单请求的条件,会被浏览器判定为特殊请求,,例如请求方式为PUT。

预检请求

浏览器先询问服务器,当前网页所在的域名是否在服务器的许可名单之中,以及可以使用哪些HTTP动词和头信息字段。只有得到肯定答复,浏览器才会发出正式的XMLHttpRequest请求,否则就报错。

非简单请求会发出一次预检测请求,返回码是204,预检测通过才会真正发出请求,这才返回200。这里通过前端发请求的时候增加一个额外的headers来触发非简单请求。

在这里插入图片描述

一个“预检”请求的样板:

OPTIONS /cors HTTP/1.1 Origin: http://manage.leyou.com Access-Control-Request-Method: PUT Access-Control-Request-Headers: X-Custom-Header Host: api.leyou.com Accept-Language: en-US Connection: keep-alive User-Agent: Mozilla/5.0...

与简单请求相比,除了Origin以外,多了两个头:

Access-Control-Request-Method:接下来会用到的请求方式,比如PUT Access-Control-Request-Headers:会额外用到的头信息

预检请求的响应

服务的收到预检请求,如果许可跨域,会发出响应:

HTTP/1.1 200 OK Date: Mon, 01 Dec 2008 01:15:39 GMT Server: Apache/2.0.61 (Unix) Access-Control-Allow-Origin: http://manage.leyou.com Access-Control-Allow-Credentials: true Access-Control-Allow-Methods: GET, POST, PUT Access-Control-Allow-Headers: X-Custom-Header Access-Control-Max-Age: 1728000 Content-Type: text/html; charset=utf-8 Content-Encoding: gzip Content-Length: 0 Keep-Alive: timeout=2, max=100 Connection: Keep-Alive Content-Type: text/plain

除了Access-Control-Allow-Origin和Access-Control-Allow-Credentials以外,这里又额外多出3个头:

Access-Control-Allow-Methods:允许访问的方式 Access-Control-Allow-Headers:允许携带的头 Access-Control-Max-Age:本次许可的有效时长,单位是秒,过期之前的ajax请求就无需再次进行预检了 如果浏览器得到上述响应,则认定为可以跨域,后续就跟简单请求的处理是一样的了。

请求流程 浏览器先根据同源策略对前端页面和后台交互地址做匹配,若同源,则直接发送数据请求;若不同源,则发送跨域请求。服务器收到浏览器跨域请求后,根据自身配置返回对应文件头。若未配置过任何允许跨域,则文件头里不包含 Access-Control-Allow-origin 字段,若配置过域名,则返回 Access-Control-Allow-origin + 对应配置规则里的域名的方式。浏览器根据接受到的响应头里的 Access-Control-Allow-origin 字段做匹配,若无该字段,说明不允许跨域,从而抛出一个错误;若有该字段,则对字段内容和当前域名做比对,如果同源,则说明可以跨域,浏览器接受该响应;若不同源,则说明该域名不可跨域,浏览器不接受该响应,并抛出一个错误。 webpack 代理

通过 webpack 中的 proxy 进行代理,从而解决跨域的问题。

module.exports = { //... devServer: { proxy: { '/api': { target: 'http://www.baidu.com/', pathRewrite: { '^/api': '' }, changeOrigin: true, // target是域名的话,需要这个参数, secure: false, // 设置支持https协议的代理 }, '/api2': { ..... } } } };

webpack proxy ,就是 webpack 提供的解决跨域的方案。其基本行为是接受客户端发送的请求后转发给其他的服务器,目的是为了便于开发者在开发的模式下解决跨域的问题。要想实现代理必须要一个中间服务器, webpack 提供服务器的工具是 webpack-dev-server,只适用于开发阶段。

原理( http-proxy-middleware 中间件)

​ proxy 工作原理上市利用 http-proxy-middleware 这个 http 代理中间件,实现请求转发给其他的服务器。如下:在开发阶段,本地地址是 Http://loaclhost:3000 , 该浏览器发送一个前缀带有 /api 标识的向拂去器请求数据,但是这个服务器只是将这个请求转发给另一台服务器:

const express = require('express'); const proxy = require('http-proxy-middleware'); const app = express(); app.use('/api', proxy({target: 'http://www.example.org', changeOrigin: true})); app.listen(3000); // http://localhost:3000/api/foo/bar -> http://www.example.org/api/foo/bar

在开发阶段,webpack-dev-server 会自动启动一个本地开发服务器,所以我们的应用在开发阶段是独立运行在 localhost 的一个端口上的,而后端服务器又是运行在另一个地址上。所以在开发阶段中,由于浏览器的同源策略,当本地访问的时候就会出现跨域资源请求的问题,通过设置 webpack proxy 实现代理请求后,相当于浏览器和服务器之间添加了一个代理商。当本地发送请求的时候,中间服务器会接受这个情求,并将这个请求转发给目标服务器,目标服务器返回数据后,中间服务器又会将数据返回给浏览器,当中间服务器将数据返回给服务器的时候,它们两者是同源的,并不会存在跨域的问题。服务器和服务器之间是不会存在跨域资源的问题的。

参考文章: https://juejin.cn/post/6844903976505344013 https://www.jianshu.com/p/98d4bc7565b2 https://segmentfault.com/a/1190000015597029 https://segmentfault.com/q/1010000009708151



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有