什么是跨域

前言

作为一名后端开发工程师,在给前端同事写接口的时候,经常碰到他们讲,你的接口跨域了,那么什么是跨域,这里来研究下。

什么是跨域

先来看下跨域的定义

跨域的广义定义:跨域是指一个域下的文档或脚本试图去请求另一个域下的资源。

我们经常遇到的跨域是由浏览器同源策略限制的一类请求场景。

栗如,下面的请求就发生了跨域,在京东的 H5 页面中请求淘宝的接口

上面栗子中跨域最终的罪魁祸首就是浏览器的同源策略。

1、因为上面的域名不相同,所以请求的接口被认为是非同源。

2、同时出于安全性,浏览器限制脚本内发起的跨源 HTTP 请求。 例如,XMLHttpRequest 和 Fetch API 遵循同源策略。所以上面的请求就报了跨域的错误。

同源策略

什么是同源策略

同源策略/SOP (Same origin policy)是一种约定,由 Netscape 公司1995年引入浏览器。

同源策略是指在 Web 浏览器中,允许某个网站脚本访问另一个网站的数据,但前提是这两个网站必须满足三个相同:

1、协议相同;

2、域名相同;

3、端口相同;

一旦两个网站满足上述条件,这两个网站就被认定为具有相同来源。

非同源的限制条件

如果两个网站非同源,将受到下面的几种限制

1、Cookie、LocalStorage 和 IndexDB 无法读取;

2、DOM 无法获得;

3、AJAX 请求不能发送。

同源策略的目的

同源政策的目的,是为了保证用户信息的安全,防止恶意的网站窃取数据。

栗如:Cookie 中存了一些用户的登陆信息,如果没有同源策略的限制,那么任何网站都能访问 Cookie,用户的信息就会泄露了,就能伪造用户信息登陆目标网站了,这显然是有很大的安全隐患的。

如何处理跨域

因为非同源有上面三种限制,这种能够规避一定的安全问题,但是有时候我们正常的使用也受到了影响,所以有时候我们需要想办法规避这种限制。

非同源限制中的

1、Cookie、LocalStorage 和 IndexDB 无法读取;

2、DOM 无法获得;

这里就不展开讨论了,这种只需要在前端页面中进行调整就行了,这里重点关注下有和后端交互的 3、AJAX 请求不能发送 的这种限制。

处理 AJAX 非同源的限制

同源政策规定,AJAX 请求只能发给同源的网址,否则就报错。

除了架设服务器代理(浏览器请求同源服务器,再由后者请求外部服务),有三种方法规避这个限制。

1、JSONP

2、WebSocket

3、CORS

关于 WebSocket 和 JSONP 的处理参见浏览器同源政策及其规避方法

作为服务端开发,对于 CORS 使用的比较多,这里展开讨论下

CORS

CORS 是一个 W3C 标准,全称是"跨域资源共享"(Cross-origin resource sharing)。

它允许浏览器向跨源服务器,发出 XMLHttpRequest 请求,从而克服了 AJAX 只能同源使用的限制。

跨源资源共享 (CORS)(或通俗地译为跨域资源共享)是一种基于 HTTP 头的机制,该机制通过允许服务器标示除了它自己以外的其它 origin(域,协议和端口),使得浏览器允许这些 origin 访问加载自己的资源。跨源资源共享还通过一种机制来检查服务器是否会允许要发送的真实请求,该机制通过浏览器发起一个到服务器托管的跨源资源的"预检"请求。在预检中,浏览器发送的头中标示有 HTTP 方法和真实请求中会用到的头。

实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信。

CORS 的使用的关键在服务端,浏览器发送请求,服务端接收到客户端请求做一些判断(请求方是否在自己的“白名单”里?),如果没问题就返回数据,否则拒绝。

浏览器将 CORS 请求分成两类:

简单请求(simple request)

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

C/C++Linux服务器开发高级架构师/C++后台开发架构师​免费学习地址https://ke.qq.com/course/417774?flowToken=1013189

另外还整理一些C++后台开发架构师 相关学习资料,面试题,教学视频,以及学习路线图,免费分享有需要的可以自行添加:Q群:720209036 点击加入~ 群文件共享,详情看以下视频

Linux C/C++后台开发学习资源整理,包括教学视频,文档,面试题,学习路线图

简单请求(simple request)

某些请求不会触发 CORS 预检请求。这样的请求为“简单请求”,只要同时满足下面的两大条件就是简单请求。

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

  • HEAD

  • GET

  • POST

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

  • Accept

  • Accept-Language

  • Content-Language

  • Last-Event-ID

  • Content-Type:只限于三个值 application/x-www-form-urlencoded、multipart/form-data、text/plain

简单请求基本流程

简单请求没有预检的流程,所以浏览器只会发送一次请求,发出的 CORS 请求,头信息中会有一个 Origin 字段。

GET /cors HTTP/1.1
Origin: http://api.bob.com
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...

Origin 字段用来说明,本次请求来自哪个源(协议 + 域名 + 端口)。服务器根据这个值,决定是否同意这次请求。

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

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

Access-Control-Allow-Origin: http://api.bob.com
Access-Control-Allow-Credentials: true
Access-Control-Expose-Headers: FooBar
Content-Type: text/html; charset=utf-8

上面以 Access-Control- 开头的都是和 CORS 请求相关的字段。

Access-Control-Allow-Origin

该字段是必须的。它的值要么是请求时Origin字段的值,要么是一个*,表示接受任意域名的请求。

Access-Control-Allow-Credentials

该字段可选。它的值是一个布尔值,表示是否允许发送 Cookie。默认情况下,Cookie 不包括在 CORS 请求之中。设为 true,即表示服务器明确许可,Cookie 可以包含在请求中,一起发给服务器。这个值也只能设为 true,如果服务器不要浏览器发送 Cookie,删除该字段即可。

Access-Control-Expose-Headers

该字段可选。CORS 请求时,XMLHttpRequest 对象的 getResponseHeader() 方法只能拿到6个基本字段:Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma。如果想拿到其他字段,就必须在 Access-Control-Expose-Headers 里面指定。上面的例子指定,getResponseHeader('FooBar') 可以返回 FooBar 字段的值。

withCredentials 属性

CORS请求默认不发送 Cookie 和 HTTP 认证信息。如果要把 Cookie 发到服务器,一方面要服务器同意,指定 Access-Control-Allow-Credentials 字段。

Access-Control-Allow-Credentials: true

另一方面,开发者必须在AJAX请求中打开 withCredentials 属性。

var xhr = new XMLHttpRequest();
xhr.withCredentials = true;

否则,即使服务器同意发送 Cookie,浏览器也不会发送。或者,服务器要求设置 Cookie,浏览器也不会处理。

但是,如果省略 withCredentials 设置,有的浏览器还是会一起发送 Cookie。这时,可以显式关闭 withCredentials。

xhr.withCredentials = false;

需要注意的是,如果要发送 Cookie,Access-Control-Allow-Origin 就不能设为星号,必须指定明确的、与请求网页一致的域名。同时,Cookie 依然遵循同源政策,只有用服务器域名设置的 Cookie 才会上传,其他域名的 Cookie 并不会上传,且(跨源)原网页代码中的 document.cookie 也无法读取服务器域名下的 Cookie。

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

不同时满足上面简单请求的两大条件的就是非简单请求

比如请求方法是 PUT 或 DELETE,或者 Content-Type 字段的类型是 application/json。

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

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

使用下面的脚本来请求追书的 H5 页面 https://www.zhuishushenqi.com

var url = 'https://api.zhuishushenqi.com/captcha/register?type=geetest';
var xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.setRequestHeader('x-device-id', 'test');
xhr.send();

​可以看到发送了两次请求的信息,一次的类型是 preflight 也就是预检请求

预检信息

预检信息的请求

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

OPTIONS /captcha/register?type=geetest HTTP/1.1
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Access-Control-Request-Headers: x-device-id
Access-Control-Request-Method: GET
Connection: keep-alive
Host: api.zhuishushenqi.com
Origin: https://www.zhuishushenqi.com
Referer: https://www.zhuishushenqi.com/

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

1、Access-Control-Request-Method

该字段是必须的,用来列出浏览器的 CORS 请求会用到哪些 HTTP 方法,上例是 GET。

2、Access-Control-Request-Headers

该字段是一个逗号分隔的字符串,指定浏览器 CORS 请求会额外发送的头信息字段,上例是 x-device-id。

预检信息的响应

来看下正常的响应,使用上面的脚本在淘宝的 H5 页面进行访问

服务器收到"预检"请求以后,检查了Origin、Access-Control-Request-Method和Access-Control-Request-Headers字段以后,确认允许跨源请求,就可以做出回应。

HTTP/1.1 200 OK
Allow: GET,HEAD
Content-Type: text/html; charset=utf-8
Accept-Ranges: bytes
Access-Control-Allow-Origin: https://www.zhuishushenqi.com
Access-Control-Allow-Credentials: true
Access-Control-Allow-Methods: GET,POST,PUT,DELETE,OPTIONS
Access-Control-Allow-Headers: Content-Type,x-app-name,x-device-id

上面的 HTTP 回应中,关键的是 Access-Control-Allow-Origin 字段,表示 https://www.zhuishushenqi.com 可以请求数据,不会产生跨域的错误。该字段也可以设为 * 号,表示同意任意跨源请求。

Access-Control-Allow-Origin: *

如果服务器否定了"预检"请求,会返回一个正常的 HTTP 回应,但是没有关键信息 Access-Control-Allow-Origin 的头信息字段。这时,浏览器就会认定,服务器不同意预检请求,因此触发一个错误,被 XMLHttpRequest 对象的 onerror 回调函数捕获。控制台就会打印跨域的报错信息,例如,文中示例 1 中的跨域报错。

Access to XMLHttpRequest at 'https://h5api.m.taobao.com/h5/mtop.user.getusersimple/1.0' from origin 'https://www.jd.com' has been blocked by CORS policy:
Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.

分析下正常返回的几个属性

1、Access-Control-Allow-Methods

它的值是逗号分隔的一个字符串,表明服务器支持的所有跨域请求的方法。注意,返回的是所有支持的方法,而不单是浏览器请求的那个方法。这是为了避免多次"预检"请求。

2、Access-Control-Allow-Headers

如果浏览器请求包括 Access-Control-Request-Headers 字段,则 Access-Control-Allow-Headers 字段是必需的。它也是一个逗号分隔的字符串,表明服务器支持的所有头信息字段,不限于浏览器在"预检"中请求的字段。

3、Access-Control-Allow-Credentials

该字段与简单请求时的含义相同。

4、Access-Control-Max-Age

该字段可选,用来指定本次预检请求的有效期,单位为秒。上面结果中,有效期是1天(86400秒),即允许缓存该条回应86400秒(即1天),在此期间,不用发出另一条预检请求。

正常信息的响应和返回

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

​下面是"预检"请求之后,浏览器的正常CORS请求。

GET /captcha/register?type=geetest HTTP/1.1
Accept: */*
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Connection: keep-alive
Host: api.zhuishushenqi.com
Origin: https://www.zhuishushenqi.com
Referer: https://www.zhuishushenqi.com/
x-device-id: test

上面头信息的Origin字段是浏览器自动添加的。

下面是服务器正常的回应。

HTTP/1.1 200 OK
Cache-Control: no-store
Content-Length: 129
Content-Type: application/json; charset=utf-8
Access-Control-Allow-Origin: https://www.zhuishushenqi.com
Access-Control-Allow-Credentials: true
Access-Control-Allow-Methods: GET,POST,PUT,DELETE,OPTIONS
Access-Control-Allow-Headers: Content-Type,x-app-name,x-device-id

服务端代码如何处理

这里使用的是 GO 中的 gin 框架,来看下后端对 CORS 跨源通信的处理

var origins = map[string]bool{"http://test.hello.com": true,"https://test.hello.com": true,
}func Cross() gin.HandlerFunc {return func(c *gin.Context) {origin := c.Request.Header.Get("Origin")if origins[origin] {c.Writer.Header().Set("Access-Control-Allow-Origin", origin)c.Writer.Header().Set("Access-Control-Allow-Credentials", "true")c.Writer.Header().Set("Access-Control-Allow-Methods", "GET, HEAD, POST, PUT, PATCH, DELETE, CONNECT, OPTIONS, TRACE")c.Writer.Header().Set("Access-Control-Allow-Headers", "Authorization, Content-Type")}if c.Request.Method == "OPTIONS" {c.AbortWithStatus(200)return}c.Next()}
}

如何调试跨域

了解完什么是跨域之后,来学习下如何快速方便的调试跨域

目前浏览器在使用 ajax 技术上都是使用 XMLHttpRequest(XHR) 对象来对服务器进行交互。

所以使用 XMLHttpRequest 就能调试跨域请求

var url = 'https://h5api.m.taobao.com/h5/mtop.user.getusersimple/1.0';
var xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.setRequestHeader('X-Custom-Header', 'value');
xhr.send();

在需要处理跨域的 H5 页面的的 Console 中执行上面的脚本即可,如果有跨域就会报错。例如文中图一的示例。

原文地址:什么是跨域,后端工程师如何处理跨域 - ZhanLi - 博客园

什么是跨域,后端工程师如何处理跨域相关推荐

  1. 后端工程师的「跨域」之旅

    跨域,对后端工程师来说,可谓既熟悉又陌生. 这两个月我以架构师的角色参与一款教育产品的孵化,有了一段难忘的跨域之旅. 写这篇文章,我想分享我在跨域这个知识点上的经历和思考,希望对大家有所启发. 1 遇 ...

  2. 【前端44_前后端交互_跨域】前端解决:JSONP、后端解决:CORS 、后端代理

    文章目录 跨域 什么是跨域 前端解决: JSONP 实现原理 步骤 前端:创建标签,拼接传递参数 后端:接收值,返回值 封装 Ajax 代码 在封装的 Ajax 中添加 JSONP 需求 思路 练习: ...

  3. Django后端彻底解决跨域问题

    Django后端彻底解决跨域问题 参考文章: (1)Django后端彻底解决跨域问题 (2)https://www.cnblogs.com/skyflask/p/10675706.html 备忘一下.

  4. 解决java前后端分离端口跨域问题

    解决java前后端分离端口跨域问题 参考文章: (1)解决java前后端分离端口跨域问题 (2)https://www.cnblogs.com/mollie-x/p/10449686.html 备忘一 ...

  5. Spring Boot2.x-13前后端分离的跨域问题解决方法之Nginx

    文章目录 概述 浏览器同源策略 后台搭建 pom.xml interceptor 配置 Controller 启动测试 浏览器和session 后端工程发布到服务器上 问题复现 通过Nginx反向代理 ...

  6. (79)FPGA如何处理跨时钟域问题-面试必问(三)(第16天)

    (79)FPGA如何处理跨时钟域问题-面试必问(三)(第16天) 1 文章目录 1)文章目录 2)FPGA初级课程介绍 3)FPGA初级课程架构 4)FPGA如何处理跨时钟域问题-面试必问(三)(第1 ...

  7. 后端CORS解决跨域问题

    后端CORS解决跨域问题 参考文章: (1)后端CORS解决跨域问题 (2)https://www.cnblogs.com/zmc940317/p/10164775.html 备忘一下.

  8. go设置后端启动_Vue 之前后端分离的跨域

    阅读本文约需要9分钟 大家好,我是你们的导师,我每天都会在这里给大家分享一些干货内容(当然了,周末也要允许老师休息一下哈).上次老师跟大家分享了JS 之继承的知识,今天跟大家分享下Vue 之前后端分离 ...

  9. 后端配置了跨域配置前端访问还是提示跨域

    后端配置文件: @Configuration public class WebAppConfiguration implements WebMvcConfigurer {/*** 解决跨域问题**/@ ...

最新文章

  1. Apriori算法进行关联分析实战
  2. 【PHP】字符串去空格并将每个单词首字母转换成大写de多种解法
  3. JavaWeb--MVC案例1-------(4)删除
  4. C++绝不在构造和析构过程中调用virtual函数
  5. vue检测对象值_Vue 不能检测到对象属性的添加或删除,注意!!!
  6. C++语言第一课的学习
  7. 使用 pqgrid 将JSON数据转换成TABLE
  8. 箱线图怎么判断异常值_极简统计学---箱线图[2]
  9. while循环python的范围_为什么Python中的range()循环比使用while循环更快?
  10. Web常用对象(2)
  11. matlab鲍威尔方法求函数,基于MATLAB的鲍威尔法求极值问题.doc
  12. 极路由第三方插件大全_极路由极硬货HC5663春节折腾记
  13. 数独游戏代码C++解法
  14. html640设计稿,移动设备分辨率(终于弄懂了为什么移动端设计稿总是640px和750px)...
  15. 思科2960交换机光口激活失败,提示has bad crc,解决方法
  16. 小程序开发API之mDNS
  17. 大数据新时代依然需要古老的磁带存储技术
  18. STAF学习系列--安装及配置
  19. 手游立项(一):理解手游开发
  20. 想开发手机APP软件,首先要弄清楚以下10点

热门文章

  1. python装饰器怎么设置_这个python装饰器怎么了?
  2. 复习JAVA高级部分
  3. excel拆分为多个sheet工作表或多个单独的excel文件。超好用
  4. NLP基础--文本特征提取中文分词word2vec原理
  5. jave Duration: N/A, bitrate: N/A
  6. vue首屏加载速度慢_vue项目首屏打开速度慢的解决方法
  7. 第二届全国大学生算法设计与编程挑战赛(赛题,共10个)
  8. SAS初级编程系列视频:第三章编辑和调试SAS程序
  9. 学计算机设计制图需啥基础,学CAD制图需要什么软件
  10. python-opencv 边缘检测