CORS简介_Cross-Origin Resource Sharing

本文介绍了CORS(跨源资源共享)的概念及其工作原理,并通过示例详细解释了如何使用CORS实现跨域请求。同时对比了CORS与其他跨域访问技术的区别。

  现在请跟我做:在您的浏览器的地址栏中输入www.yhd.com并敲击回车。在网站内容全部加载完毕后,按F12打开浏览器的调试窗口。当切换到Sources页时,您会发现您当前所看到的一号店的页面是从多个不同的域中得到的:

  或许有些读者会感到奇怪:在之前自己 写网页的时候就曾经尝试访问非当前域中的资源,却怎么也不成功,一号店是如何做到的?

  当然,这不是一号店的独门绝技,而仅仅是使用了一些跨域访问的技术而已。而在本文中,我们就将对一种跨域访问技术CORS(Cross-Origin Resource Sharing)进行介绍。

 

为什么要用CORS

  在需要做出一个技术决定时,我们常常需要给出适当的理由。就CORS而言,使用它的根本原因就是要完成资源的跨域访问,也就是如何绕过Same-origin Policy。

  那么什么是Same-origin Policy呢?简单地说,在一个浏览器中访问的网站不能访问另一个网站中的数据,除非这两个网站具有相同的Origin,也即是拥有相同的协议、主机地址以及端口。一旦这三项数据中有一项不同,那么该资源就将被认为是从不同的Origin得来的,进而不被允许访问。

  但是这个限制的确过于严格了:一个大型网站常常拥有一系列子域。在这些域之间交换数据就会受到Same-origin Policy的限制。为了绕过该限制,业界提出了一系列解决该问题的方法,例如更改document.domain属性,跨文档消息,JSONP以及CORS等。这些解决方案各有各的长处,因此我们需要根据需求的不同来对这些方案进行选择。

  可以说更改document.domain属性的方法是最为直接快速的的方法,也较为常见。通过将从不同域中得到的脚本的document.domain属性设置为同一个值,就可以使得这些脚本之间可以相互交互。例如从“http://blog.ambergarden.com”得到的网页可以通过执行如下的脚本改变其document.domain属性中记录的所属域:

1 document.domain = ‘ambergarden.com’;

  那么接下来,该脚本就可以访问ambergarden.com中的数据了。

  这种方法也有其自身的劣势,那就是软件开发人员不可以随便设置document.domain属性的值,至少在一些浏览器上是如此的。

  跨文档消息则是通过向Window实例发送消息来完成的。在使用时,软件开发人员需要通过调用一个Window的postMessage()函数来向该Window实例发送消息。此时Window实例内部的onmessage事件将被触发,进而使得该事件的消息处理函数被调用。但是在接收到消息的时候,消息处理函数首先需要判断消息来源的合法性,以避免恶意用户通过发送消息的方式来非法执行代码。

  JSONP则是通过在文档中嵌入一个<script>标记来从另一个域中返回数据。例如在页面中添加一个如下的<script>标记:

1 <script src="http://blog.ambergarden.com/someData?callback=some_func"/>

  该<script>标记会向http://blog.ambergarden.com/someData发送一个GET请求。在数据返回到客户端后,some_func()函数将会被调用。当然,这种方法拥有一个显著的缺点,那就是只支持GET操作。

  就如您刚刚看到的一样,上面所列出的各个方法各自有各自的缺点及局限性。而相较于这些方法,CORS则没有那么多工作需要去做,也没有那么多限制。因此在本文中,我们将主要对CORS进行讲解。

 

CORS运行流程

  现在我们就来看一个通过CORS来进行跨域访问的简单示例。假设ambergarden.com想从一个公有数据平台public-data.com中返回一些数据,那么在页面逻辑中,其可以通过下面的代码向public-data.com发送数据请求:

1 function retrieveData() {
2     var request = new XMLHttpRequest();
3     request.open('GET', 'http://public-data.com/someData', true);
4     request.onreadystatechange = handler;
5     request.send();
6 }

  在运行这段代码的之后,浏览器会向服务发送如下的请求:

1 GET /someData/ HTTP/1.1
2 Host: public-data.com
3 ......
4 Referer: http://ambergarden.com/somePage.html
5 Origin: http://ambergarden.com

  而一个支持CORS协议的服务可能会给出下面的响应:

1 HTTP/1.1 200 OK
2 Access-Control-Allow-Origin: http://ambergarden.com
3 Content-Type: application/xml
4 ......
5 
6 [Payload Here]

  这里有一个值得注意的响应头:Access-Control-Allow-Origin。该响应头用来记录可以访问该资源的域。在接收到服务端响应后,浏览器将会查看响应中是否包含Access-Control-Allow-Origin响应头。如果该响应头存在,那么浏览器会分析该响应头中所标示的内容。如果其包含了当前页面所在的域,那么浏览器就将知道这是一个被允许的跨域访问,从而不再根据Same-origin Policy来限制用户对该数据的访问。

  从整个访问数据的流程来看,用户所使用的跨域访问数据的脚本实际上和普通的访问同一个域中数据的脚本并没有什么不同。而不同的,仅仅是在响应中多了一个Access-Control-Allow-Origin响应头。

  是不是很简单?实际上我们展示的仅仅是最为简单的Simple Request的执行流程。而CORS则将导致跨域访问的请求分为三种:Simple Request,Preflighted Request以及Requests with Credential。

  如果一个请求没有包含任何自定义请求头,而且它所使用HTTP动词是GET,HEAD或POST之一,那么它就是一个Simple Request。但是在使用POST作为请求的动词时,该请求的Content-Type需要是application/x-www-form-urlencoded,multipart/form-data或text/plain之一。

  如果一个请求包含了任何自定义请求头,或者它所使用的HTTP动词是GET,HEAD或POST之外的任何一个动词,那么它就是一个Preflighted Request。如果POST请求的Content-Type并不是application/x-www-form-urlencoded,multipart/form-data或text/plain之一,那么其也是Preflighted Request。

  一般情况下,一个跨域请求不会包含当前页面的用户凭证。一旦一个跨域请求包含了当前页面的用户凭证,那么其就属于Requests with Credential。

  前面我们已经看过浏览器对Simple Request是如何进行处理的。那么接下来我们就来看看Preflight Request是如何执行的。相较于Simple Request,Preflight Request的运行流程则略为复杂一些。

  假设现在我们要向公有数据平台public-data.com写入一些数据,那么我们就需要发送一个POST请求:

复制代码
1 function sendData() {
2     var request = new XMLHttpRequest(),
3         payload = ......;
4     request.open('POST', 'http://public-data.com/someData', true);
5     request.setRequestHeader('X-CUSTOM-HEADER', 'custom_header_value');
6     request.onreadystatechange = handler;
7     request.send(payload);
8 }
复制代码

  在执行了该段代码之后,浏览器首先发出的请求将如下所示:

1 OPTIONS /someData/ HTTP/1.1
2 Host: public-data.com
3 ......
4 Origin: http://ambergarden.com
5 Access-Control-Request-Method: POST
6 Access-Control-Request-Headers: X-CUSTOM-HEADER

  可以看到,我们首先发送的并不是POST请求,而是OPTION请求。该请求还通过Access-Control-Request-Method以及Access-Control-Request-Headers标示了请求类型以及请求中所包含的自定义HTTP Header。实际上,它相当于向服务端询问访问资源的权限:“您好,我想向你这里发送数据,你看可以吗?”。而在真正访问资源前发送一个请求进行探测也是该请求被称为是Preflight Request的原因。

  在服务端看到该OPTIONS请求后,其将分析该请求中的内容并返回一个响应,以通知浏览器是否允许向它发送数据:

1 HTTP/1.1 200 OK
2 Access-Control-Allow-Origin: http://ambergarden.com
3 Access-Control-Allow-Methods: POST, GET, OPTIONS
4 Access-Control-Allow-Headers: X-CUSTOM_HEADER
5 Access-Control-Max-Age: 1728000
6 ......

  浏览器分析该响应并了解到其被允许向服务端发送数据以后,其才会向服务端发送真正的POST请求:

1 POST /someData/ HTTP/1.1
2 Host: public-data.com
3 X-CUSTOM-HEADER: custom_header_value
4 ......
5 
6 [Payload Here]

  而服务端则会接收并处理该请求:

1 HTTP/1.1 200 OK
2 Access-Control-Allow-Origin: http://ambergarden.com
3 Content-Type: application/xml
4 ......
5 
6 [Payload Here]

  最后一种请求Requests with Credential的运行流程则和前两种请求类似。只不过在发送请求的时候,我们需要将用户凭证包含在请求中:

复制代码
1 function retrieveData() {
2     var request = new XMLHttpRequest();
3     request.open('GET', 'http://public-data.com/someData', true);
4     request.withCredentials = true;
5     request.onreadystatechange = handler;
6     request.send();
7 }
复制代码

  而在服务端的响应中,其将拥有一个额外的Access-Control-Allow-Credentials响应头:

1 HTTP/1.1 200 OK
2 Access-Control-Allow-Origin: http://ambergarden.com
3 Content-Type: application/xml
4 ......
5 
6 [Payload Here]

 

集成对CORS的支持

  从上面的示例中已经能够看到,在使用CORS来访问数据的时候,客户端不需要更改任何数据访问逻辑。所有的一切工作都是在服务端及浏览器之间自动完成的。因此如果希望为一个系统集成CORS支持的时候,我们需要做的工作主要集中在服务端。

  当然,集成工作实际上十分简单:在你的web.xml中添加一个Filter(或利用已有的Filter)并根据传入的请求首先判断其是哪一种CORS请求。在得知了请求的类型后,我们就可以决定到底以哪种方式响应用户了。这里的逻辑较为简单,因此我就不再赘述了。

 

转载请注明原文地址并标明转载:http://www.cnblogs.com/loveis715/p/4592246.html

<think> 首先,用户的问题是:"我想查找后端使用CORSCross-origin resource sharing)解决跨域问题的方法"。用户要求我回答后端使用CORS解决跨域问题的方法。 我需要参考提供的引用,这些引用来自站内引用,包括[^1], [^2], [^3], [^4]。 - 引用[^1] 讨论了CORS基础原理,包括漏洞利用和预防措施。它提到正确配置跨域请求。 - 引用[^2] 具体展示了如何在SpringBoot中使用CORS来解决前后端分离的跨域问题,包括前端代码和后端实现代码。 - 引用[^3] 解释了CORS的工作原理,强调它是浏览器和服务器共同支持的,并且服务器是实现的关键。 - 引用[^4] 简要介绍了CORS作为跨域访问技术,对比了其他方法。 用户的问题焦点是后端使用CORS解决跨域问题的方法。所以,我应该从后端角度解释如何实现CORS。 从引用中提取关键点: - 从[^3]:CORS需要服务器实现CORS接口。浏览器自动处理,但服务器必须配置响应头来允许跨域请求。 - 从[^2]:具体例子在SpringBoot中,后端可以通过添加CORS配置来解决。后端代码示例展示了如何设置响应头。 - 从[^1]:正确配置CORS包括只允许受信任的站点,避免空源等。 现在,结构化我的回答: 1. **解释CORS是什么**:简要说明CORS是浏览器机制,允许跨域请求,但需要服务器设置响应头。 2. **后端实现方法**:详细描述后端如何设置响应头。 - 关键响应头: - `Access-Control-Allow-Origin`: 指定允许的源(如 `*` 或具体域名)。 - `Access-Control-Allow-Methods`: 指定允许的HTTP方法(如 GET, POST)。 - `Access-Control-Allow-Headers`: 指定允许的请求头。 - `Access-Control-Allow-Credentials`: 指示是否允许发送凭据(如 cookies)。 3. **具体示例**:参考[^2],展示SpringBoot中的实现。例如,在Java中使用注解或配置类。 - 在[^2]中,后端代码通过设置响应头来实现。具体代码可能涉及添加 `@CrossOrigin` 注解或配置过滤器。 4. **一般步骤**:无论使用什么技术栈,后端都需要: - 在响应中添加必要的CORS头。 - 处理预检请求(OPTIONS方法),因为浏览器在发送实际请求前可能发送预检请求。 5. **最佳实践**:参考[^1],确保安全配置: - 不要使用通配符 `*` 在生产环境,除非必要。 - 只允许特定的可信域名。 - 避免将 `null` 列入白名单。 - 在内部网络中也避免使用通配符。 6. **引用来源**:在回答中使用引用标识如[^1], [^2]等,自然添加在段落末尾。 回答结构: - 用中文回答。 - 保持清晰,逐步解释。 - 使用内联数学表达式如 $...$ 如果涉及公式,但这里可能不涉及数学,所以可能不需要。 - 独立公式如果需要,但同样,这里可能不需要。 - 生成相关问题在末尾。 最后,生成相关问题: - 基于主题,如CORS的其他方面、常见错误、与其他跨域方法的比较等。 大纲响应: 1. **引言**:解释CORS及其作用。 2. **后端实现方法**: - 设置响应头。 - 处理预检请求。 - 框架具体示例(如SpringBoot)。 3. **安全注意事项**。 4. **总结**。 5. **相关问题**。 确保回答真实可靠,基于引用。 </think> ### 后端使用CORS解决跨域问题的方法 跨域资源共享(CORSCross-Origin Resource Sharing)是浏览器的一种安全机制,用于限制不同源(协议、域名、端口)之间的资源访问。如果未经正确配置,浏览器会阻止跨域请求(如前端应用在 `http://localhost:3000` 访问后端 `http://api.example.com`),导致常见错误如“CORS policy”问题。CORS的核心在于**后端服务器设置响应头**来显式允许特定源的访问,而浏览器会自动处理这些头信息。以下从后端角度分步说明实现方法,参考了站内提供的引用内容。 #### 1. **CORS的基本工作原理** CORS依赖于HTTP响应头来控制跨域请求。浏览器在发送跨域请求时,会自动添加 `Origin` 头(如 `Origin: http://localhost:3000`),后端需在响应中返回特定的CORS头来授权该请求。关键点包括: - **简单请求**:对于GET、HEAD或POST(且Content-Type为 `application/x-www-form-urlencoded`、`multipart/form-data` 或 `text/plain`),浏览器直接发送请求,后端只需设置 `Access-Control-Allow-Origin`。 - **预检请求(Preflight)**:对于非简单请求(如PUT、DELETE或自定义头),浏览器先发送OPTIONS方法预检请求,后端需响应并设置CORS头来确认允许实际请求[^3]。 - CORS的实现完全由服务器驱动,开发者无需修改前端代码(前端AJAX调用与同源请求相同)[^3]。 #### 2. **后端实现CORS的关键步骤** 后端主要通过配置HTTP响应头来实现CORS。以下是通用的方法(适用于任何后端框架如Spring Boot、Node.js、Django等): **步骤1: 设置核心响应头** - **`Access-Control-Allow-Origin`**:指定允许访问的源。可以是具体域名(如 `http://localhost:3000`)或通配符 `*`(允许所有源,但不安全)。 - 示例响应头:`Access-Control-Allow-Origin: http://localhost:3000` - 在代码中动态设置:根据请求的 `Origin` 头判断是否在允许列表中。 - **`Access-Control-Allow-Methods`**:指定允许的HTTP方法(如GET、POST、PUT)。 - 示例:`Access-Control-Allow-Methods: GET, POST, PUT` - **`Access-Control-Allow-Headers`**:指定允许的自定义请求头(如 `Authorization`)。 - 示例:`Access-Control-Allow-Headers: Content-Type, Authorization` - **`Access-Control-Allow-Credentials`**:如需传递cookies或认证信息,设为 `true`(但 `Access-Control-Allow-Origin` 不能为 `*`)。 - 示例:`Access-Control-Allow-Credentials: true` - 对于预检请求(OPTIONS方法),后端需返回这些头并设置状态码为204(No Content)。 **步骤2: 处理预检请求** - 后端需拦截OPTIONS请求,并在响应中添加上述头信息。 - 示例伪代码: ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值