目录
一:什么是跨域
跨域是指浏览器在向一个服务器发送请求时,该请求的地址与当前页面的地址不同,即协议、域名、端口号中至少有一个不同,导致浏览器出于安全考虑,阻止了页面与请求之间的交互。
二:为什么会跨域
跨域问题的出现是由于浏览器的同源策略(Same-Origin Policy)所引起的。同源策略是浏览器最核心也最基本的安全策略之一,它是指浏览器只允许与当前网页的协议、域名、端口号完全相同的资源进行交互。
同源策略的存在是出于安全考虑,目的是为了防止恶意的脚本攻击,保障用户信息的安全。如果浏览器允许跨域访问,那么攻击者就可以在自己的网站上注入一些脚本,然后诱导用户在访问其他网站时自动执行这些脚本,从而达到攻击的目的。因此,同源策略是浏览器保护用户安全的重要措施之一。
三:跨域的解决方案
1.代理服务器
在日常的项目开发中,解决跨域问题最常使用的方案是使用代理服务器
代理服务器解决跨域问题其实是利用了同源策略只受限于浏览器访问服务器,对于服务器访问服务器并没有限制的特点,作为中间服务器做了一个请求转发的功能。
具体来说,就是当前端网页在浏览器中发起网络请求时,其实这个请求是发送到代理服务器上的,然后代理服务器会将请求转发给目标服务器,再将目标服务器返回的响应转发给客户端。
值得一提的是,在生产环境和开发环境中,我们通常会使用不同的方式实现代理
1.1.生产环境
在线上环境下,我们一般会采用nginx来做反向代理
下面是通过 Nginx 反向代理解决跨域问题的步骤:
1.修改 Nginx 的配置文件,在 http 块中添加以下内容:
http {
# 允许跨域请求的地址
add_header 'Access-Control-Allow-Origin' '*';
# 允许跨域请求的方法
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS';
# 允许跨域请求的请求头
add_header 'Access-Control-Allow-Headers' 'Origin, X-Requested-With, Content-Type, Accept, Authorization';
# 允许跨域请求的请求头中可以携带的信息
add_header 'Access-Control-Expose-Headers' 'Authorization';
# 允许跨域请求携带 Cookie
add_header 'Access-Control-Allow-Credentials' 'true';
...
}
2.在 server 块中添加以下内容:
server {
listen 80;
server_name example.com;
# 将 /api 转发到 http://api.example.com
location /api {
proxy_pass http://api.example.com;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
这个配置表示将客户端请求的 /api 转发到 http://api.example.com,并将客户端请求的头信息中的 Host 和 X-Real-IP 添加到后端请求的头信息中。这样就可以通过 Nginx 反向代理解决跨域问题了。
通过 Nginx 反向代理解决跨域问题的优点是可以集中管理跨域请求,缺点是需要额外的服务器和维护成本。
1.2.开发环境
在开发环境下,我们可以通过修改本地开发服务器的配置文件或者在前端代码中添加跨域相关的代码来解决跨域问题,下面我以Vue中的项目为例
如果是通过vue-cli脚手架工具搭建项目,我们可以通过webpack为我们起一个本地服务器作为请求的代理对象。在vue.config.js文件,新增以下代码
module.exports = {
devServer: {
proxy: {
'/api': { // '/api'是代理标识,用于告诉node,url前面是/api的就是使用代理的
target: "http://xxx.xxx.xx.xx:8080", //目标地址,一般是指后台服务器地址
changeOrigin: true, //是否跨域
pathRewrite: { // pathRewrite 的作用是把实际Request Url中的'/api'用""代替
'^/api': ""
}
}
}
}
}
2.JSONP
JSONP(JSON with Padding)是一种跨域解决方案,它利用 script 标签的 src 属性不受同源策略限制的特性,通过在服务端返回 JSON 数据的同时,将数据包裹在一个回调函数中返回给前端,从而实现跨域请求
下面是使用 JSONP 的示例代码:
function jsonp(url, callback) {
const script = document.createElement('script');
script.src = url + '&callback=' + callback;
document.body.appendChild(script);
}
function handleData(data) {
console.log(data);
}
jsonp('http://example.com/api/data?param=value', 'handleData');
在上述代码中,我们定义了一个 jsonp 函数,它接收一个 URL 和回调函数名作为参数。在函数内部,我们创建了一个 script 标签,并将 URL 和回调函数名拼接到其中。最后,将该 script 标签添加到文档中,从而触发跨域请求。
虽然 JSONP 能够解决跨域的问题,但是它也存在一些缺点,主要包括以下几点:
-
仅支持 GET 请求:JSONP 是通过动态创建 script 标签实现的,因此只支持 GET 请求,无法支持 POST 请求和其他类型的请求。
-
安全性问题:JSONP 的安全性问题比较容易被攻击者利用,攻击者可以通过修改 JSONP 的回调函数来注入恶意代码,从而对网站造成安全威胁。
-
无法处理错误:由于 JSONP 是通过 script 标签加载数据的,因此无法捕获请求失败的情况,无法处理错误信息。
总的来说,JSONP 是一种比较古老的跨域解决方案,虽然它能够解决一部分跨域问题,但是由于存在以上缺点,因此在现代化的应用中,通常采用其他更加安全、可靠的跨域解决方案,例如 CORS、反向代理等。
3.CORS
CORS (Cross-Origin Resource Sharing,跨域资源共享),它由一系列传输的HTTP头组成,这些HTTP头决定浏览器是否阻止前端 JavaScript 代码获取跨域请求的响应
CORS 实现起来非常方便,只需要增加一些 HTTP 头,让服务器能声明允许的访问来源 。只要后端实现了 CORS,就实现了跨域
以koa框架举例 添加中间件,直接设置Access-Control-Allow-Origin响应头
app.use(async (ctx, next)=> {
ctx.set('Access-Control-Allow-Origin', '*');
ctx.set('Access-Control-Allow-Headers', 'Content-Type, Content-Length, Authorization, Accept, X-Requested-With , yourHeaderFeild');
ctx.set('Access-Control-Allow-Methods', 'PUT, POST, GET, DELETE, OPTIONS');
await next()
})
CORS(跨域资源共享)是目前较为常用的一种跨域解决方案,它相对于 JSONP 等传统的跨域解决方案,有以下优点和缺点:
优点:
-
安全性高:CORS 在浏览器端和服务器端都支持,能够在保证安全性的前提下,允许不同源的客户端请求服务器端资源,避免了 JSONP 可能存在的安全问题。
-
支持所有类型的HTTP请求:CORS 不仅支持简单请求(GET、POST、HEAD)的跨域请求,还支持复杂请求(PUT、DELETE、OPTIONS、PATCH)等其他类型的跨域请求。
-
灵活性高:CORS 支持配置多种不同类型的请求头和响应头,具有更高的灵活性和扩展性。
-
与标准相符:CORS 是 W3C 标准,与现代 Web 开发技术相符,能够支持更多的跨域场景。
缺点:
-
配置复杂:CORS 的配置比较复杂,需要在服务器端进行一定的设置和配置,对于一些不熟悉 CORS 的开发人员来说,可能需要花费更多的时间和精力。
-
兼容性问题:CORS 是现代 Web 开发技术的一部分,旧版本的浏览器可能不支持或支持不完全,需要进行兼容性处理。
-
存在预检请求:对于一些复杂的请求(如带有自定义头信息的请求),浏览器会发送一个预检请求(Preflight),增加了请求的开销和网络传输的负担。
综上所述,CORS 是一种相对于传统跨域解决方案更加安全、灵活和标准的解决方案,但是配置比较复杂,兼容性和预检请求等问题需要进行处理。