前端跨域

前端跨域有多少种情况

  • CORS 跨域
  • jsonp 跨域
  • postMessage
  • document.domain 一般情况下我们使用比较多的就是 Cross-Originjsonp 这两种方式。postMessagedocument.domain 使用的不是很多。下面我会介绍他们如何使用和使用的场景还有一些不常见的问题。

CORS

全拼 cross-origin resource sharing,意思是跨域资源共享。我们先看看浏览器的兼容性,如下图:

注意: 所以要使用 CORS 进行跨域的话,必须注意客户端和服务器必须同时支持。

浏览器将 CORS 请求分成两类:简单请求(simple request)和非简单请求(not-so-simple request),我们看看如何区分

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

  • HEAD
  • GET
  • POST

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

  • Accept
  • Accept-Language
  • Content-Language
  • Last-Event-ID
  • Content-Type :只限于三个值 application/x-www-form-urlencodedmultipart/form-data、text/plain

简单请求(simple request)

  • 对于简单请求,浏览器直接发出CORS请求。具体来说,就是在头信息之中,增加一个Origin字段。Origin字段用来说明,本次请求来自哪个源(协议 + 域名 + 端口)。服务器根据这个值,决定是否同意这次请求。

  • 如果 Origin 指定的源,不在许可范围内,服务器会返回一个正常的 HTTP 回应。浏览器发现,这个回应的头信息没有包含 Access-Control-Allow-Origin 字段(详见下文),就知道出错了,从而抛出一个错误,被 XMLHttpRequestonerror 回调函数捕获。注意,这种错误无法通过状态码识别,因为 HTTP 回应的状态码有可能是200。(如果我们使用的 fetch,那么 fetch 会自动触发一个错误,那么这个时候我们可以通过 promise.catch() 方法可捕获这个错误信息)。

  • 如果 Origin 指定的域名在许可范围内,服务器返回的响应,会多出几个头信息字段

  // 该字段是必须的。它的值要么是请求时Origin字段的值,要么是一个*,表示接受任意域名的请求Access-Control-Allow-Origin: http://api.bob.com// 该字段可选。它的值是一个布尔值,表示是否允许发送Cookie,如果需要发送cookie那么就设置为true,否则不会含有这个字段// 如果要发送cookie,那么还需要设置 var xhr = new XMLHttpRequest(); xhr.withCredentials = true;Access-Control-Allow-Credentials: true// 该字段可选。CORS请求时,XMLHttpRequest对象的getResponseHeader()方法只能拿到6个基本字段:// Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma// 如果想拿到其他字段,就必须在Access-Control-Expose-Headers里面指定。上面的例子指定// getResponseHeader('FooBar')可以返回FooBar字段的值。Access-Control-Expose-Headers: FooBar

需要注意的是,如果要发送 CookieAccess-Control-Allow-Origin就不能设为*,必须指定明确的、与请求网页的域名一致。

非简单请求(not-so-simple request)

  • 非简单请求的 CORS 请求,会在正式通信之前,增加一次 HTTP 查询请求,称为 预检请求(preflight)。

  • 预检请求 用的请求方法是 OPTIONS,表示这个请求是用来询问的。头信息里面,关键字段是 Origin,表示请求来自哪个源。

  • 除了 Origin 字段,"预检"请求的头信息包括两个特殊字段

  // 该字段是必须的,用来列出浏览器的CORS请求会用到哪些HTTP方法,上例是PUT。Access-Control-Request-Method: PUT// 该字段是一个逗号分隔的字符串,指定浏览器CORS请求会额外发送的头信息字段Access-Control-Request-Headers: X-Custom-Header
  • 服务器收到预检请求以后,检查了 Origin、Request-MethodRequest-Headers 字段以后,确认允许跨源请求,就可以做出回应。如果浏览器否定了"预检"请求,会返回一个正常的 HTTP 回应,但是没有任何 CORS 相关的头信息字段。这时,浏览器就会认定,服务器不同意预检请求,因此触发一个错误,被 XMLHttpRequest 对象的 onerror 回调函数捕获
  // 该字段必需, 字段也可以设为星号,表示同意任意跨源请求Access-Control-Allow-Origin: *// 该字段必需,它的值是逗号分隔的一个字符串,表明服务器支持的所有跨域请求的方法。// 注意,返回的是所有支持的方法,而不单是浏览器请求的那个方法。这是为了避免多次"预检"请求。Access-Control-Allow-Methods: GET, POST, PUT// 如果浏览器请求包括Access-Control-Request-Headers字段,则Access-Control-Allow-Headers字段是必需的。// 它也是一个逗号分隔的字符串,表明服务器支持的所有头信息字段,不限于浏览器在"预检"中请求的字段。Access-Control-Allow-Headers: X-Custom-Header// 该字段可选// 该字段与简单请求时的含义相同Access-Control-Allow-Credentials: true// 该字段可选,用来指定本次预检请求的有效期,单位为秒。// 即允许缓存该条回应1728000秒(即20天),在此期间,不用发出另一条预检请求。Access-Control-Max-Age: 1728000

一旦服务器通过了"预检"请求,以后每次浏览器正常的 CORS 请求,就都跟简单请求一样,会有一个 Origin 头信息字段。服务器的回应,也都会有一个 Access-Control-Allow-Origin 头信息字段。

我们以 koa为列,在demo中这么设置就可以了

  // 下面是对cors进行的设置,在返回的headers中添加如下字段,也可以使用 koa2-corsapp.use(async (ctx, next) => {// 允许来自所有域名请求,请求携带了cookie就不能设置为 *ctx.set("Access-Control-Allow-Origin", "http://10.105.16.162:3000");// 设置所允许的HTTP请求方法ctx.set("Access-Control-Allow-Methods", "GET,POST");// 它也是一个逗号分隔的字符串,表明服务器支持的所有头信息字段.ctx.set("Access-Control-Allow-Headers", "x-requested-with,accept,origin,content-type");// 该字段可选。它的值是一个布尔值,表示是否允许发送Cookie。默认情况下,Cookie不包括在CORS请求之中。// 当设置成允许请求携带cookie时,需要保证"Access-Control-Allow-Origin"是服务器有的域名,而不能是"*";ctx.set("Access-Control-Allow-Credentials", true);await next();});  

jonsp 跨域

jonsp跨域的原理是通过动态创建script的标签的形式来实现跨域的,因为script不受同源策略的影响。但是jsonp只能接受get方法。 看看下面的代码是如何封装的,并做了超时设置。

const defaultOptions = {timeout: 15000,uid: 0,perfix: '_jsp',name: 'callBack',
}
const jsp = (url, params, options = {}) => {let timer = null;const options = Object.assign({}, defaultOptions, options);const callBackName = `${options.perfix}_${options.name}_${options.uid++}`;const paramsKeys = Object.entries(params);url += `?${callback}=${callBackName}`;url = paramsKeys.reduce((initUrl, [k, v]) => {return `${initUrl}&${k}=${v}`;}, url);const body = document.getElementByTagName(body)[0];const script = document.createElement('script');script.src = url;body.appendChild(script);const clean = () => {if (timer) {clearInterval(timer);timer = null;body.removeChild(script);window[callBackName] = null;}};return new Promise((resolve, reject) => {window[callBackName] = function(data){clear();return resolve(data);};timer = setTimeout(() => {clear();return reject('请求超时了');}, options.time);});
}

postMessage (这是HTML5提供的方法,IE8+才支持) 可以实现两个窗口之间的通信,是不是同源不重要

它可用于解决以下方面的问题:

  • 页面和其打开的新窗口的数据传递
  • 多窗口之间消息传递
  • 页面与嵌套的iframe消息传递

使用方法:postMessage(data, origin) 方法接受两个参数 data html5规范支持任意基本类型或可复制的对象,但部分浏览器只支持字符串,所以传参时最好用 JSON.stringify()序列化。IE10才开始支持对象形式。 origin 协议+主机+端口号,也可以设置为 *,表示可以传递给任意窗口,如果要指定和当前窗口同源的话设置为 /

  // 发送窗口代码:const ifr = document.createElement('iframe');ifr.src = 'http://localhost:3001';ifr.display = 'none';document.body.append(ifr);ifr.onload  = function(){const doc = ifr.contentWindow;// 目标源 可以限定具体的 协议 + 域名 + 端口 或者是 *const targetOrigin = 'http://localhost:3001';// 发送消息 doc可以是window.open() 也可以是嵌套的iframe// 发送的消息可以是 string 或者 object(IE10才支持)doc.postMessage('hello world', targetOrigin);}// 接收窗口代码:window.addEventListener('message', (e)=> { // 必须是window不能是document监听// 数据const data = e.data;// 消息是从哪个源发送来的const origin = e.origin;// 发送消息窗口const source = e.source;// 通过source回传消息 同样在发送消息的窗口也需要监听source.postMessage('hi shenxuxiang', 'http://localhost:3000');},false)

document.domain

此方案仅限主域相同,子域不同的跨域应用场景。实现原理:两个页面都通过js强制设置document.domain为基础主域,就实现了同域。 之后就可以共享window下的属性。

  // www.a.com/a.html页面中的代码document.domain = 'a.com';const ifr = document.createElement('iframe');ifr.src = 'http://www.script.a.com/b.html';ifr.display = 'none';document.body.append(ifr);ifr.onload  = function(){const doc = ifr.contentWindow;console.log(doc.name); // shenxuxiang}// www.script.a.com/b.html页面中的代码document.domain = 'a.com';window.name = 'shenxuxiang';

上面一共介绍了4种前端跨域的方法,jsonpcors 可以和后台进行跨域,而 postMessagedomain 适合页面间的通讯。页面间的跨域还有可以使用 window.name | location.hash。如果想了解的可以点击这里

参考

Cross-Origin Resource Sharing MDN

转载于:https://juejin.im/post/5ceded75f265da1b5e72d4d3

常用的前端跨域的几种方式相关推荐

  1. 解决前端跨域的几种方法

    解决前端跨域的几种方法 了解跨域出现的原因 解决跨域的几种方法 想要解决跨域 先要知道为什么会出现跨域 跨域:指的是浏览器不能执行其他网站的脚本 它是由浏览器的同源策略造成的 是浏览器对javascr ...

  2. 前端实现跨域的三种方式

    前端解决跨域的三种方式: 1.cors跨域(只需要后端配置) header("Access-Control-Allow-Origin:*"); // 允许任何来源 header(& ...

  3. 只有ajax会跨域吗_ajax处理跨域有几种方式

    jQuery 使用 JSONP 缺点: 1.这种方式无法发送post请求(这里) 2.另外要确定jsonp的请求是否失败并不容易,大多数框架的实现都是结合超时时间来判定. 跨域的几种方式 在项目中可能 ...

  4. SpringBoot解决跨域的5种方式

    本文来说下SpringBoot中实现跨域的5种方式. 文章目录 什么是跨域 java解决CORS跨域请求的方式 返回新的CorsFilter(全局跨域) 重写WebMvcConfigurer(全局跨域 ...

  5. 什么是同源策略及解决跨域的三种方式

    同源策略 1.1.1 所谓同源是指"协议+域名+端口"三者相同,即便两个不同的域名指向同一个 ip 地址,也非同源.同源策略/SOP(Same origin policy)是一种约 ...

  6. 前端交互之“解决前端跨域的三种方法”

    1.什么是前端跨域? 跨域是浏览器为了安全而做出的限制策略:浏览器请求必须遵从同源测试: http://www.bilibili.com:8080:/anime/?key=calue路径 键值对 同协 ...

  7. php 跨域读php_php跨域的几种方式

    PHP实现跨域的几种形式 1.JSONP(JSON with padding)原理 利用html里面script标签可以加载其他域下的js这一特性,使用script src的形式来获取其他域下的数据, ...

  8. 前端处理跨域的几种方式

    什么是跨域? 跨域是指一个域下的文档或脚本试图去请求另一个域下的资源,这里跨域是广义的. 广义的跨域: 1.资源跳转:A链接.重定向.表单提交 2.资源嵌入: <link>.<scr ...

  9. 后端技术:SpringBoot 中实现跨域的5种方式

    作者:ratelfu blog.csdn.net/weter_drop/article/details/112135940 一.为什么会出现跨域问题 出于浏览器的同源策略限制.同源策略(Sameori ...

最新文章

  1. 使用Powershell管理Linux 下的 SQL Server
  2. hihoCoder-1830 2018亚洲区预选赛北京赛站网络赛 C.Cheat 模拟
  3. PHP-SESSION深入理解
  4. Mongodb实现多表join
  5. web.xml 基本配置
  6. luogu P1702 突击考试
  7. 初学Python之谈
  8. 思博伦安全专家预测2017年民用和军用全球导航应用面临的更大风险
  9. ubuntu下安装JDK以及配置
  10. 电信无线服务器的密码是什么,电信光猫wifi默认密码是多少?
  11. 扫一扫二维码隐私权政策
  12. javaScript、jQuery、html实现九宫格拼图游戏(逻辑及源码)
  13. 网页版pdf转换方法
  14. matlab拟合曲线poly交点,matlab 离散曲线求交点
  15. Photoshop 抠图方式
  16. Python绘图实例3:正八边形绘制
  17. Windows10中Edge浏览器突然出现“无法访问该页面”问题的解决方案
  18. 递归、迭代和分治(1):递归
  19. AIOT人工智能物联网+项目实战
  20. 2021计算机研究生秋招总结

热门文章

  1. (计算机组成原理)第四章指令系统-第一节2:扩展操作码
  2. Qt之设置QWidget背景色
  3. PyTorch之torch.nn.Softmax()
  4. Springboot之actuator配置不当漏洞(autoconfig、configprops、beans、dump、env、health、info、mappings、metrics、trace)
  5. Python traceback模块的使用(抛异常、报错、输出错误)
  6. 线性表:链栈算法实现
  7. js获取数组前n项的和
  8. Windows 10 x64 安装 Visual Basic 6.0 SP6
  9. [转]UE/UI/UCE/UED的区别 附UED团队网站链接
  10. 哪些数据库是行存储?哪些是列存储?有什么区别?