什么是跨域
由于浏览器的同源策略影响,当请求源与请求地址存在协议、域名、端口有任何一个不同时,就会引起跨域
注意:跨域是浏览器引起的
例如:
请求源 | 请求地址 | 是否跨域 |
---|---|---|
http://localhost:3000 | http://localhost:3001 | 是,端口不同 |
https://www.baidu.a.com | https://www.baidu.b.com | 是,域名不同 |
http://localhost:3000 | https://localhost:3000 | 是,协议不同 |
http://localhost:3000 | http://localhost:3000/api | 否 |
怎么解决跨域
- JSONP
- CORS
- Nginx代理
使用JSONP实现跨域
利用script标签没有跨域限制的漏洞实现跨域
具体实现步骤:
1、提前定义好用来接收数据的函数
2、与后端约定好传入的字段,并把之前定义好用来接收数据的函数名作为参数传递给后端
3、后端接收到参数后,把数据放入到函数的参数拼接返回
4、浏览器识别到函数执行,获得结果
// node模拟后端
const express = require('express');
const app = express();
app.use(express.static('./public'));
app.get('/api', (req, res) => {
// 后端约定字段名为callback,值为前端获取数据的函数名
// 这里获取到前端传递过来的参数名,之后前端会通过这个函数获取到参数
const callback = req.query.callback;
const data = {
name: 'name',
age: 'age'
};
// 最后结果会是一个为getData(data) {}的函数,data就是传递给前端的数据
res.send(`${callback}(${JSON.stringify(data)})`);
});
// 运行服务器
app.listen(3000, () => {
console.log('服务器启动成功');
});
<button id="button">发送JSONP请求</button>
<script type="text/javascript">
// 提前定义好获取数据的函数
function getData(data) {
console.log('data', data); // { name: 'name', age: 'age' }
}
let button = document.getElementById('button');
button.addEventListener('click', () => {
const script = document.createElement('script');
// 将函数名传递给后端
const url = 'http://localhost:3000/api?callback=getData';
script.src = url;
// 动态创建script标签
document.getElementsByTagName('head')[0].appendChild(script);
});
</script>
使用CORS(跨域资源共享)解决跨域
CORS是一个W3C标准,全称是跨域资源共享(Cross-origin-resource-sharing)
它允许浏览器向跨源服务器发起Ajax请求,从而克服了Ajax只能同源使用的限制
CORS需要浏览器和服务器同时支持,也是目前主流的跨域解决方案之一
两种请求
浏览器将CORS请求分为两类,简单请求和复杂请求。
简单请求
(1)请求方法属于下面之一
- HEAD
- GET
- POST
(2)HTTP的头信息不超过以下内容
- Accept
- Accept-language
- Content-language
- Content-Type的值为application/x-www-form-urlencoded、multipart/form-data、text-plain
### 复杂请求
不满足简单请求的就属于简单请求
CORS解决跨域的步骤
如果是简单请求,浏览器会自动在请求头中添加一个Origin字段,用来表明此次请求来自哪个源(协议 + 域名 + 端口)
如果是复杂请求,浏览器会在正式请求发送前发送一个option预检请求。预检请求相当于一个缩小版的真实请求,其中会带上真实请求的请求源地址、请求方法、自定义请求头等一些信息向服务器询问这些内容是否在服务器的允许范围内
服务器收到请求后,如果是简单请求,origin指定的源不在允许范围内的话,服务器会返回一个正常的http响应,但是不包含Access-Control-Allow-Origin。浏览器收到响应后发现不存在Access-Control-Allow-Origin字段,就知道出错了,然后抛出错误。反之则请求成功。
如果是复杂请求,浏览器收到预检请求后会检查预检请求中携带的信息,确认允许后才会做出回应。一旦服务器通过了预检请求,解下的复杂请求都会像简单请求一样会有一个origin请求头信息,服务器的回应也都会有一个Access-Control-Allow-Origin响应头信息
与JSONP相比
CORS与JSONP的使用目的相同,但是比JSONP更强大。JSONP只支持GET请求,CORS支持所有类型的HTTP请求
。JSONP的优势在于支持老式浏览器,以及可以向不支持CORS的网站请求数据。
使用Nginx代理解决跨域
简单使用Nginx代理实现跨域,具体步骤如下:
1、配置nginx监听客户端发送的请求,然后转发到后端
2、客户端将原本发送给后端的请求地址改为nginx监听的地址
3、服务端收到请求进行处理
1、简单配置一下nginx
server {
# 监听端口
listen 5001;
# 监听的地址
server_name localhost;
# 上面2个配置好后客户端的请求地址就改为server_name + listen,也就是localhsot:5001
# 处理 /list接口
location /list {
# 添加CORS头,允许所有域;always代表每次响应都携带头信息
add_header Access-Control-Allow-Origin '*' always;
# 转发地址
proxy_pass http://localhost:3000/;
}
}
2、简单配置一个接收请求的服务器
const express = require('express');
const app = express();
app.get('/list', (req, res) => {
res.send('请求成功');
});
app.listen(3000, () => {
console.log('服务器启动成功');
});
3、客户端发起请求
<button id="request">发送请求</button>
<script>
let request = document.getElementById('request');
request.addEventListener('click', () => {
// 请求不再直接发送到后端(http://localhost:3000),而是发送到nginx代理服务器,由nginx代 理转发给后端
fetch('http://localhost:5001/list')
.then(res => res.text())
.then(res => {
console.log(res);
})
});
</script>