JavaScript CORS
什么是CORS?
CORS(Cross-Origin Resource Sharing,跨源资源共享)是一种安全机制,允许一个网站的资源(如JavaScript、API等)在浏览器中请求另一个不同"源"(origin)的资源。
在了解CORS之前,我们首先要理解同源策略(Same-Origin Policy)。
同源策略
同源策略是浏览器的一种重要安全机制,它限制了来自一个源的文档或脚本如何与另一个源的资源进行交互。"同源"指的是相同的协议、主机名和端口号。
例如:
https://example.com/page1.html
和https://example.com/page2.html
是同源的https://example.com
和http://example.com
不同源(协议不同)https://example.com
和https://api.example.com
不同源(子域名不同)https://example.com
和https://example.com:8080
不同源(端口不同)
备注
同源策略是浏览器的默认行为,它阻止JavaScript从一个源向另一个源发送请求,以保护用户免受恶意网站的攻击。
为什么需要CORS?
随着Web应用的发展,单一源的限制变得越来越不实用。例如:
- 前端应用可能需要访问托管在不同域名下的API
- 微服务架构中,各个服务可能运行在不同的域名上
- CDN(内容分发网络)常常使用不同的域名
CORS提供了一种安全的方式来放宽同源策略的限制,允许特定的跨域请求。
CORS的工作原理
CORS通过HTTP头信息来工作,允许服务器声明哪些源可以访问其资源。
简单请求
满足以下条件的请求被称为"简单请求":
- HTTP方法是
GET
、HEAD
或POST
- 只包含被允许的头部信息
- Content-Type只限于
application/x-www-form-urlencoded
、multipart/form-data
或text/plain
对于简单请求,浏览器会自动添加Origin
头部,服务器会返回Access-Control-Allow-Origin
头部来指示允许哪些源访问资源。
// 前端代码 - 简单请求示例
fetch('https://api.example.com/data', {
method: 'GET',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
})
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Error:', error));
服务器响应示例:
HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://mywebsite.com
Content-Type: application/json
{"message": "这是来自跨域API的数据"}
预检请求
对于非简单请求(如使用PUT
、DELETE
方法,或发送application/json
格式的数据),浏览器会先发送一个OPTIONS预检请求(preflight request)。
预检请求的流程:
-
浏览器自动发送OPTIONS请求,包含以下头部:
Origin
: 请求的源Access-Control-Request-Method
: 实际请求将使用的HTTP方法Access-Control-Request-Headers
: 实际请求将发送的自定义头部
-
服务器响应是否允许该请求,通过返回:
Access-Control-Allow-Origin
: 允许的源Access-Control-Allow-Methods
: 允许的HTTP方法Access-Control-Allow-Headers
: 允许的头部Access-Control-Max-Age
: 预检请求 的缓存时间
-
如果预检请求被允许,浏览器才会发送实际请求
// 前端代码 - 会触发预检请求的例子
fetch('https://api.example.com/data', {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
'X-Custom-Header': 'value'
},
body: JSON.stringify({ key: 'value' })
})
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Error:', error));
携带凭证的请求
默认情况下,跨域请求不会发送cookies或HTTP认证信息。如果需要发送这些凭证,你需要:
- 在前端设置
credentials: 'include'
- 服务器必须设置
Access-Control-Allow-Credentials: true
- 服务器的
Access-Control-Allow-Origin
不能使用通配符*
,必须明确指定源
// 前端代码 - 带凭证的请求
fetch('https://api.example.com/user-data', {
method: 'GET',
credentials: 'include' // 发送cookies等凭证
})
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Error:', error));
服务器响应:
HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://mywebsite.com
Access-Control-Allow-Credentials: true
Content-Type: application/json
{"username": "john_doe", "email": "[email protected]"}
常见的CORS错误和解决方案
错误信息解读
当CORS策略阻止请求时,浏览器会在控制台输出类似以下的错误:
Access to fetch at 'https://api.example.com/data' from origin 'https://mywebsite.com'
has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on
the requested resource.