一、跨域是什么
跨域本质是浏览器基于同源策略的一种安全手段
同源策略(Sameoriginpolicy),是一种约定,它是浏览器最核心也最基本的安全功能
所谓同源(即指在同一个域)具有以下三个相同点
- 协议相同(protocol)
- 主机相同(host)
- 端口相同(port)
反之非同源请求,也就是协议、端口、主机其中一项不相同的时候,
比如我浏览器自己的服务器 http://127.0.0.1:2108/,浏览器请求的接口是 http://localhost:8888/test/,当请求地址的 传输协议 域名 端口号 这三个有任意一个不同,浏览器就会认为这是别人家的服务器我们的浏览器就会给咱们一个报错:Access to XMLHttpRequest ,这时候就会产生跨域。
二、如何解决
解决跨域的方法有很多,下面列举了三种:
- JSONP(前端)
- CORS (后端)
- Proxy (后端)
而在vue
项目中,我们主要针对CORS
或Proxy
这两种方案进行展开
- jsonp
10年前火爆前端的跨域解决方案,至今还在使用,这个解决方案绕过了我们的XMLHttp。
JSONP(JSON with Padding)是一种跨域数据交换的方法,主要用于解决由于同源策略导致的浏览器无法从不同源(域名、协议、端口)加载资源的问题。它利用了<script>
标签不受同源策略限制的特性来实现跨域请求。
工作原理
1.客户端请求:客户端通过一个<script>
标签发起请求,请求的URL中包含一个回调函数的名称。
<script src="http://example.com/data?callback=myCallback"></script>
这里的myCallback
是客户端定义的回调函数
2.服务器响应:服务器接收到请求后,将数据包装在客户端指定的回调函数中返回。
myCallback({"name": "John", "age": 30});
3.数据接收:客户端的回调函数被执行,从而接收到服务器返回的数据。
示例
假设客户端需要从一个不同的源获取数据:
客户端代码:
<script>
function myCallback(data) {
console.log(data.name); // 输出: John
console.log(data.age); // 输出: 30
}
</script>
<script src="http://example.com/data?callback=myCallback"></script>
服务器端代码(使用Node.js):
const http = require('http');
http.createServer((req, res) => {
const callback = req.url.split('=')[1];
const data = { name: 'John', age: 30 };
res.writeHead(200, { 'Content-Type': 'text/javascript' });
res.end(`${callback}(${JSON.stringify(data)});`);
}).listen(8080);
限制与安全
- 限制:JSONP只支持GET请求,不支持POST等其他HTTP方法。
- 安全:由于JSONP允许执行任意的JavaScript代码,因此存在安全风险,比如跨站脚本攻击(XSS)。使用时需要确保数据来源的安全性。
2.cors
CORS(Cross-Origin Resource Sharing,跨源资源共享)是一种基于HTTP头信息的机制,允许不同源的Web页面安全地访问和操作另一个源的资源。这种机制通过服务器响应中的特定HTTP头信息来告知浏览器是否允许跨源请求。
跨域问题的背景
由于浏览器的同源策略,Web应用中的资源(如脚本、图片等)只能从加载它们的源(即协议、域名、端口)加载。这限制了不同源之间的资源访问,从而防止了某些类型的安全漏洞,但也限制了Web应用的灵活性。
CORS的实现机制
-
预检请求(Preflight Request):
- 当浏览器检测到一个跨源请求可能会触发非简单请求(如POST、PUT等)时,它会先发送一个预检请求(OPTIONS方法)到服务器,询问服务器是否允许这种跨源请求。
- 预检请求会包含一些HTTP头信息,如
Access-Control-Request-Method
(请求方法)和Access-Control-Request-Headers
(请求头)。
-
服务器响应:
- 服务器接收到预检请求后,需要在响应中返回相应的CORS头信息,明确指示是否允许跨源请求。
- 常见的CORS响应头信息包括:
Access-Control-Allow-Origin
:指定允许访问的源,可以是具体的URL或*
(表示允许所有源)。Access-Control-Allow-Methods
:指定允许的HTTP方法。Access-Control-Allow-Headers
:指定允许的请求头。Access-Control-Allow-Credentials
:指示是否允许发送Cookie
具体实现
1.方案一:使用插件
在我们的服务器代码里安装cors模块
npm i cors
在接口里引入
var cors = require('cors')
app.use(cors())
2.方案二:在后端服务器上书写可以运行哪些请求头可以访问我的服务器
app.use(function (req, res, next) {
// req:表示请求对象
// res:响应对象
// next:表示下一步
res.setHeader('Access-Control-Allow-Origin', '*') //允许哪些域名请求我
res.setHeader('Access-Control-Request-Methods', 'GET,POST,PUT,DELETE,OPTIONS')// 允许哪些请求方式请求我的服务器
res.setHeader('Access-Control-Allow-Headers', 'x-requested-with,content-type')//允许携带哪些请求头信息
// 上面三行相当于服务器开启跨域资源共享
next()
})
3.Proxy
代理(Proxy)也称网络代理,是一种特殊的网络服务,允许一个(一般为客户端)通过这个服务与另一个网络终端(一般为服务器)进行非直接的连接,因为因为同源策略是限制客户端的,服务器之间没有限制,一些网关、路由器等网络设备具备网络代理功能。一般认为代理服务有利于保障网络终端的隐私或安全,防止攻击
方案一
如果是通过vue-cli
脚手架工具搭建项目,我们可以通过webpack
为我们起一个本地服务器作为请求的代理对象
通过该服务器转发请求至目标服务器,得到结果再转发给前端,但是最终发布上线时如果web应用和接口服务器不在一起仍会跨域
在vue.config.js
文件,新增以下代码
amodule.exports = {
devServer: {
host: '127.0.0.1',
port: 8084,
open: true,// vue项目启动时自动打开浏览器
proxy: {
'/api': { // '/api'是代理标识,用于告诉node,url前面是/api的就是使用代理的
target: "http://xxx.xxx.xx.xx:8080", //目标地址,一般是指后台服务器地址,也就是要请求的地址
changeOrigin: true, //是否跨域
pathRewrite: { // pathRewrite 的作用是把实际Request Url中的'/api'用""代替
'^/api': ""
}
}
}
}
}
通过axios
发送请求中,配置请求的根路径,这样当请求'/api',相应的地址的时候,就会向目标服务器发送请求,pathRewrite可以不写。
axios.defaults.baseURL = '/api'
方案二
此外,还可通过服务端实现代理请求转发
下载代理请求的第三方插件
npm i http-proxy-middleware
以express
框架为例
var express = require('express');
const proxy = require('http-proxy-middleware')
const app = express()
app.use(express.static(__dirname + '/'))
app.use('/api', proxy({ target: 'http://localhost:4000', changeOrigin: false
}));
module.exports = app