Last Updated on 2024-07-26 by likun.gong
跨域是什么
跨域只发生在浏览器的访问中,发生跨域时,通常会看到 CORS error 等字眼,并且请求返回了 403 状态码。
为什么会跨域
在理解跨域之前,要先知道浏览器中的同源策略。
同源策略:是指两个页面具有相同的协议、地址、端口,那么它们就是同源,只要有一个不同,就不是同源。不同源之间的访问,就会产生跨域。
例如 https://a.com 页面里边的 JavaScript 脚本去访问 https://b.com ,就会产生跨域的情况。
同源策略的目的是为了保护用户隐私和数据安全,但是我们还是有需要进行合法的跨域访问的。比如我们的图片资源都是存在一个独立的 CDN 资源中,其他业务都统一来这个域名访问资源,这之间就会产生跨域的情况。
如何解决跨域
目前最常见解决跨域的办法,就是通过 CORS(跨域资源共享)。
CORS(跨域资源共享):通过设置HTTP头来允许跨域请求。
在上述的例子中,a.com 如何知道自己能够跨域访问 b.com 呢?它会在发起 GET、POST 等这些请求前,先发起一个 preflight 的请求,也就是 OPTIONS 请求,根据返回判断 b.com 是否允许自己访问。
需要注意的一点是,并不是所有的跨域都会发起 preflight 请求,同时满足以下情况是不会发起 OPTIONS 请求的:
- 请求方法是 GET、POST、HEAD
- 请求中没有特殊的请求头
跨域主要涉及的请求头信息:
- Access-Control-Allow-Origin 用于设置允许跨域请求源地址 (预检请求和正式请求在跨域时候都会验证)
- Access-Control-Allow-Headers 跨域允许携带的特殊头信息字段 (只在预检请求验证)
- Access-Control-Allow-Methods 跨域允许的请求方法或者说HTTP动词 (只在预检请求验证)
- Access-Control-Allow-Credentials 是否允许跨域使用cookies,如果要跨域使用cookies,可以添加上此请求响应头,值设为true(设置或者不设置,都不会影响请求发送,只会影响在跨域时候是否要携带cookies,但是如果设置,预检请求和正式请求都需要设置)
安全起见,Access-Control-Allow-Origin 一般不会设置 * 对所有开放,而是指定域名开放
Nginx中解决跨域
在 Nginx 的配置中,需要处理 OPTIONS 请求,同时对于 OPTIONS 之后的请求也需要添加对应的响应头
server {
listen 80;
server_name example.com;
location / {
# 允许来自任何源的请求
add_header 'Access-Control-Allow-Origin' '*';
# 允许的请求方法
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
# 允许的请求头
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
# 允许浏览器缓存预检请求结果的时间(以秒为单位)
add_header 'Access-Control-Max-Age' 1728000;
# 允许浏览器暴露的响应头
add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
# 处理 OPTIONS 预检请求
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'text/plain; charset=utf-8';
add_header 'Content-Length' 0;
return 204;
}
# 其他配置...
}
}
Cloudflare中解决跨域
在 cloudflare 中,可以通过配置规则来增加跨域的响应头,但是规则无法处理 OPTIONS 请求。
这里我使用 cloudflare worker 来解决,它类似一个代理,通过 serverless 运行,免费版每天有 10w 次请求
addEventListener('fetch', event => {
event.respondWith(handleRequest(event.request))
})
async function handleRequest(request) {
// 允许的 origin 列表
const allowedOrigins = ['https://a.xxx.com','https://b.xx.com','https://c.xxx.com', 'http://172.19.59.219:8080']
// 获取请求的 origin
const requestOrigin = request.headers.get('Origin')
// 检查是否为允许的 origin
const isAllowedOrigin = allowedOrigins.includes(requestOrigin)
// 如果是 OPTIONS 请求
if (request.method === 'OPTIONS') {
// 创建响应头
let headers = new Headers({
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
'Access-Control-Allow-Headers': 'x-url-path,content-type,x-user-token,x-language,x-job-number,x-requested-with,eagleeye-sessionid,eagleeye-pappname,eagleeye-traceid,x-data-display-token,preview-token,x-forum-token,X-Mainland,x-equipment-code',
'Access-Control-Max-Age': '86400', // 24 小时
})
// 如果是允许的 origin,设置 Access-Control-Allow-Origin
if (isAllowedOrigin) {
headers.set('Access-Control-Allow-Origin', requestOrigin)
}
// 返回 response
return new Response(null, {
status: 204,
headers: headers
})
}
// 对于非 OPTIONS 请求,继续正常处理
// 这里我们只是简单地返回原始响应,您可以根据需要修改
let response = await fetch(request)
// 如果是允许的 origin,为非 OPTIONS 请求也添加 CORS 头
if (isAllowedOrigin) {
response = new Response(response.body, response)
response.headers.set('Access-Control-Allow-Origin', requestOrigin)
}
return response
}
worker 启动之后,可以配置对应哪些域名、哪些 url 走 worker。
目前 cloudflare 新版出了个 snippets ,看起来像是 worker 的简化版,也能做到同样的事情。
发表回复