引言
在现代Web开发中,不同源之间数据交换的需求日益增长。然而,出于安全原因,浏览器对从一个域向另一个域发起请求有着严格的限制,这就是同源策略(Same-Origin Policy)。
然而,同源策略限制了后端访问接口必须与网站一致!这就非常不灵活,尤其现在web应用多为多端应用,又同时需要多个地址的服务的情况下。(这里是重点)
为了解决这一问题,并实现安全、灵活的跨域通信,跨域资源共享(Cross-Origin Resource Sharing, 简称CORS)应运而生。本文将深入探讨CORS的工作原理、配置方法及其在实际应用中的重要性。
一、同源策略与跨域限制
1. 概念
同源策略(Same-Origin Policy)是浏览器为保护用户信息安全而实施的一种安全机制。在Web开发领域,同源策略规定了来自同一来源的文档或脚本可以相互读取和交互数据,而不同源则不能直接进行这样的操作。这里的“源”由协议、域名和端口三部分组成,如果这三者完全相同,则认为两个资源是同源的。
2. 举例说明
- 同源示例
- 当前网页地址为
https://www.example.com/index.html
。 - 该页面试图通过JavaScript向
https://www.example.com/api/data
发送一个AJAX请求获取数据。
- 这两个URL具有相同的协议(HTTPS)、相同的域名(www.example.com)和相同的端口(默认HTTP/HTTPS端口,即443),因此它们被认为是同源的,这个请求将被允许执行。
- 非同源示例
- 当前网页地址为
https://www.example.com/index.html。
- 页面尝试发送一个AJAX请求到
http://api.other-example.com/data
。 - 这两个URL的协议不同(一个是HTTPS,另一个是HTTP),或者域名不同(www.example.com与api.other-example.com),因此它们被视为不同源。
- 根据同源策略,浏览器将会阻止此跨域请求,除非服务器配置了支持跨域资源共享(CORS)并明确允许从
www.example.com
发起请求。
同源策略旨在防止恶意网站通过JavaScript读取其他站点的敏感信息,例如cookies或其他存储在本地的数据。对于需要进行跨域通信的应用场景,可以通过CORS、JSONP等技术实现安全可控的数据交换。
二、跨域资源共享(CORS)
1. 概念
首先要明确的是CORS是一种W3C标准(非常重要,是浏览器和服务器共同去完成的一套http请求机制)
,允许服务器明确声明哪些外部域可以访问其资源。通过CORS,服务器能够设置允许哪些HTTP请求方法、头信息和自定义响应头被其他域使用。
跨域资源共享(Cross-Origin Resource Sharing,CORS)是一种允许网页从不同源(domain、协议或端口不一致)获取资源的机制。在Web开发中,由于浏览器的安全策略——同源策略(Same-Origin Policy),一个源下的文档或脚本默认情况下不能访问另一个源的资源。然而,在实际应用中,往往需要跨域请求以实现数据共享和服务调用。
2. CORS工作原理与举例说明
1. CORS请求类型
- 简单请求:对于GET、POST等简单HTTP方法以及某些标准头部字段的请求,可以被视为简单请求。浏览器会自动在请求头中添加Origin字段,标识请求发起者的源。
示例:
GET /data HTTP/1.1 Host: api.example.com Origin: https://www.example.net
服务器收到请求后,会在响应头中返回Access-Control-Allow-Origin
来指定哪些源可以访问该资源。
2. 预检请求(Preflight Request)
对于非简单请求,如使用自定义头部字段或PUT、DELETE等其他HTTP方法的请求,浏览器会先发送一个OPTIONS预检请求询问服务器是否接受该跨域请求。
示例:
OPTIONS /data HTTP/1.1 Host: api.example.com Origin: https://www.example.net Access-Control-Request-Method: PUT Access-Control-Request-Headers: X-Custom-Header -- Response -- HTTP/1.1 200 OK Access-Control-Allow-Origin: https://www.example.net Access-Control-Allow-Methods: PUT, POST, DELETE Access-Control-Allow-Headers: X-Custom-Header
3. 服务器配置
服务器根据接收到的请求和自身的CORS策略,通过设置特定的响应头信息来决定是否允许这次跨域请求。
例如,服务器可以通过设置如下响应头允许来自https://www.example.net
的请求:
Access-Control-Allow-Origin: https://www.example.net
4. CORS安全控制
CORS还提供了其他一些响应头用于更精细的权限控制,如:
Access-Control-Allow-Credentials
控制是否允许携带凭据(如 Cookies 或 HTTP 认证信息)进行跨域请求。Access-Control-Max-Age
设置预检请求结果的有效期,减少重复预检次数。
三、CORS请求类型与流程
1. 简单请求
- 对于GET、POST等简单HTTP方法以及一些特定的头部字段,浏览器可以直接发起跨域请求,无需预检。
浏览器会在请求头中自动添加Origin
字段表明来源域,服务器根据此信息决定是否放行请求。
在浏览器发起跨域请求时,对于非简单请求(如包含了自定义头部、PUT、DELETE等方法的请求)和所有预检请求(OPTIONS),
- 浏览器会自动在请求头中添加一个
Origin
字段。这个字段包含的是当前发出请求的页面所在的源信息,即协议+域名+端口。
如果一个网页加载自 https://example.com
,当它尝试向 https://api.example.net
发送一个跨域请求时,浏览器会在请求头中插入如下内容:
Origin: https://example.com
服务器收到请求后,可以通过检查Origin
头来判断请求是否来自允许访问的源。如果服务器配置了CORS策略,并且Access-Control-Allow-Origin
响应头中包含了与请求头中的Origin
相匹配的源,那么该请求将被放行;否则,请求会被服务器拒绝。
举个例子,服务器可以这样设置其CORS策略以允许来自https://example.com
的请求:
HTTP/1.1 200 OK Access-Control-Allow-Origin: https://example.com
2. 预检请求(Preflight Request)
- 当请求方法不是简单的GET/POST或者包含自定义头部时,浏览器会先发送一个OPTIONS方法的预检请求来询问服务器。
- 服务器收到预检请求后,若返回的响应头中包含了
Access-Control-Allow-Origin
、Access-Control-Allow-Methods
等相关字段并允许当前源,则后续的实际请求会被正常处理。
预检请求(Preflight Request)是CORS机制中的一种特殊请求,用于非简单请求(例如使用了自定义头部字段、PUT、DELETE等方法的请求)在实际发送前向服务器询问是否允许该跨域请求。以下是一个关于预检请求的例子,包括客户端发起请求和服务器端响应:
3. 客户端JavaScript示例
// 使用fetch API 发起一个带有自定义头部的PUT请求 fetch('https://api.example.com/data', { method: 'PUT', headers: { 'Content-Type': 'application/json', 'X-Custom-Header': 'custom-value' }, body: JSON.stringify({ key: 'value' }) }) .then(response => response.json()) .then(data => console.log(data)) .catch(error => console.error(error));
在此例中,由于我们设置了自定义头部X-Custom-Header
并使用了PUT方法,浏览器会先发送一个OPTIONS预检请求。
4. 服务器端响应示例(以Node.js + Express为例)
const express = require('express'); const app = express(); app.options('/data', (req, res) => { // 处理预检请求 res.set({ 'Access-Control-Allow-Origin': 'https://www.example.net', // 允许特定源 'Access-Control-Allow-Methods': 'PUT, POST, DELETE, OPTIONS', // 允许的方法列表 'Access-Control-Allow-Headers': 'Content-Type, X-Custom-Header', // 允许的头部 'Access-Control-Max-Age': 86400 // 预检结果缓存时间(单位秒) }); if (req.headers.origin) { res.setHeader('Access-Control-Allow-Credentials', true); // 允许携带凭据(如 Cookies) } res.status(204).send(); // 预检请求不需要返回数据体,仅需设置正确的响应头 }); app.put('/data', (req, res) => { // 实际处理PUT请求逻辑 }); // ...其他路由... app.listen(3000, () => console.log('Server started on port 3000'));
当客户端发起上述跨域PUT请求时,浏览器首先发送一个OPTIONS预检请求到服务器。如果服务器的预检响应表明允许该请求,则浏览器才会继续发送实际的PUT请求。
四、CORS响应头详解
Access-Control-Allow-Origin
: 必需字段,指定允许接收请求的源。Access-Control-Allow-Methods
: 指定允许的方法列表,如GET
,POST
,PUT
,DELETE
等。Access-Control-Allow-Headers
: 允许客户端发送的非标准请求头。Access-Control-Allow-Credentials
: 表示是否允许携带cookie和认证信息进行跨域请求。
Access-Control-Max-Age
: 设置预检请求结果的有效期,减少重复预检的次数。
五、CORS配置实例
在服务器端(如Node.js的Express框架),可以通过中间件实现CORS:
const express = require('express'); const app = express(); app.use((req, res, next) => { res.setHeader('Access-Control-Allow-Origin', '*'); // 允许所有源 res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE'); // 允许的方法 res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization'); // 允许的头部 if (req.method === 'OPTIONS') { res.send(204); // 预检请求则直接返回204状态码 } else { next(); // 其他请求继续执行路由处理函数 } }); // ... 添加你的路由逻辑 ...
六、CORS实践与安全性考量
尽管CORS极大地增强了Web应用程序的功能性和灵活性,但它也引入了一些潜在的安全风险,比如可能泄露敏感信息。因此,在实施CORS策略时务必谨慎,避免过于宽松的设置,如只允许特定可信的源访问资源,同时对允许的方法和头部进行严格控制。
总结来说,跨域资源共享作为现代Web开发的重要组成部分,对于构建高效、安全且具有丰富交互体验的应用至关重要。理解并正确配置CORS不仅能解决开发过程中的常见跨域问题,还能有效提升系统的安全性。
衷心感谢您阅读至此,若您在本文中有所收获,恳请您不吝点赞、评论或分享,您的支持是我们持续创作高质量内容的动力源泉。同时,也诚挚邀请您关注本博客,以便获取更多前端开发与设计相关的深度解析和实战技巧,我们期待与您共同成长,一起探索前端世界的无限精彩!