跨域

1.什么是同源策略

同源策略就是指协议,域名,端口号三者相同,浏览器就认为这个请求地址是同源的,有一个不同,则认为非同源,同源策略限制访问以下内容:

  • Cookie LocationStore SessionStore等资源不共享
  • 无法访问DOM节点
  • Ajax请求的结果会被浏览器拦截

特别说明:

-
如果是协议和端口造成的跨域问题,前端是无能为力的

-
在跨域问题上,浏览器只通过url首部(协议+域名+端口号)来识别,而不会根据域名所对应的IP地址是否相同来判断

-
跨域状态下Ajax请求是可以发出去的,只不过请求的响应被浏览器拦截了

2.跨域解决方案

1.JSONP

利用<script>标签没有跨域限制的漏洞,可以在请求地址中传入一个回调函数名,通过window监听这个回调函数,后端执行这个函数并把参数以形参的形式传入发给前端

1
2
3
4
5
6
7
8
const script = document.createElement('script');

script.src = 'http://127.0.0.1:3000/say?callback=show';

window.show = function(data) {
console.log(data)
}
document.body.appendChaild(script)
  • Nodejs
1
2
3
4
5
6
7
8
9
10
const express = require('express');
const app = express();

app.get('/say', (req, res) => {
const { callback } = req.query;

res.send(`${callback}('222')`)
})

app.listen(3000)

缺点:仅支持get方法

2.CORS

CORS需要浏览器和后端同时支持,IE8和IE9需要通过XDomainRequest来实现

浏览器会自动进行CORS通信,实现CORS通信的关键在后端,需要设置Access-Control-Allow-Origin就可以开启CORS,该属性表示哪些域名可以访问资源

使用CORS解决跨域问题,会在发送请求时出现两种情况,分别为简单请求复杂请求

简单请求

同时满足两大条件:

1.请求方法为GET POST HEAD之一

2.Content-Type的值仅限三种:text/plain multipart/form-data application/x-www-form-urlcodeed

请求中的任意 XMLHttpRequestUpload 对象均没有注册任何事件监听器; XMLHttpRequestUpload 对象可以使用 XMLHttpRequest.upload 属性访问。

复杂请求

在发送复杂请求时,会增加一次HTTP查询请求,称为”预检”请求,该请求是使用OPTION方法,通过该请求来知道服务端是否允许跨域请求

后端配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
const express = require('express')
const app = express()
const whitList - ['http://127.0.0.1:5500'] // 配置白名单

app.all('*',function (req, res, next) {
res.header('Access-Control-Allow-Origin','http://localhost:3001'); //当允许携带cookies此处的白名单不能写’*’
res.header('Access-Control-Allow-Headers','content-type,Content-Length, Authorization,Origin,Accept,X-Requested-With'); //允许的请求头
res.header('Access-Control-Allow-Methods', 'POST, GET, OPTIONS, PUT'); //允许的请求方法
res.header('Access-Control-Allow-Credentials',true); //允许携带cookies
next();
});

app.put('/getData', function(req, res) {
console.log(req.headers)
res.setHeader('name', 'jw') //返回一个响应头,后台需设置
res.end('111')
})
app.get('/getData', function(req, res) {
console.log(req.headers)
res.end('222')
})
app.use(express.static(__dirname))
app.listen(4000)

3.postMessage

poseMessage用于解决以下方面的问题:

-
页面和其打开页面的新窗口的数据传递

-
多窗口之间的消息传递

-
页面与嵌套的iframe消息传递

-
跨域数据传递

1
2
3
4
5
6
7
8
9
10
11
12
<iframe src="http://127.0.0.1:4000/b.html" frameborder="0" id="frame" onload="load()"></iframe> //等它加载完触发一个事件

<script>
window.onload = function() {
const frame = document.getElementById('frame');
frame.contentWindow.postMessage('111', 'http://127.0.0.1:4000/b.html') // 发送数据

window.onMessage = function(e) { // 监听并接收返回的数据
console.log(e.data)
}
}
</script>
1
2
3
4
5
// b.html
window.onmessage = function(e) {
console.log(e.data) //111
e.source.postMessage('222', e.origin)
}

4.webSocket

Websocket是HTML5的一个全双工双向通信协议,在建立连接之后,WebSocket的服务端与客户端都能主动向对方发送或接收数据

1
2
3
4
5
6
7
8
9
10
11
12
13
// socket.html
<script>
// 实例化socket
const socket = new WebSocket('ws://127.0.0.1:3000');
// 发送请求
socket.onopen = function () {
socket.send('111');
}
// 监听回调
socket.onmessage = function (e) {
console.log(e);
}
</script>
1
2
3
4
5
6
7
8
9
10
11
// server.js
const WebSocket = require('ws');//记得安装ws
// 实例化webSocket,监听3000端口
const wss = new WebSocket.Server({ port: 3000 });
// 建立连接
wss.on('connection', function (ws) {
ws.on('message', function (data) {
console.log(data);
ws.send('222')
});
})

5.Node中间件代理

  • 接受客户端请求
  • 将请求转发给服务器
  • 拿到服务器响应的数据
  • 将响应转发给客户端
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// server1.js 代理服务器(http://localhost:3000)
const http = require('http')
// 第一步:接受客户端请求
const server = http.createServer((request, response) => {
// 代理服务器,直接和浏览器直接交互,需要设置CORS的首部字段
response.writeHead(200, {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': '*',
'Access-Control-Allow-Headers': 'Content-Type'
})
// 第二步:将请求转发给服务器
http.request(
{
host: '127.0.0.1',
port: 4000,
url: '/',
method: request.method,
headers: request.headers
},
serverResponse => {
// 第三步:收到服务器的响应
var body = ''
serverResponse.on('data', chunk => {
body += chunk
})
serverResponse.on('end', () => {
console.log('The data is ' + body)
// 第四步:将响应结果转发给浏览器
response.end(body)
})
}
).end()
})
server.listen(3000, () => {
console.log('The proxyServer is running at http://localhost:3000')
})

6.Nginx反向代理

实现原理类似于Node中间件代理,通过Nginx配置一个代理服务器(域名与domain1相同),反向代理访问domain2接口,并且可以顺便修改cookie中的domain信息,方便当前域cookie写入,实现跨域登录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// proxy服务器
server {
listen 81;
server_name www.domain1.com;
location / {
proxy_pass http://www.domain2.com:8080; #反向代理
proxy_cookie_domain www.domain2.com www.domain1.com; #修改cookie里域名
index index.html index.htm;

# 当用webpack-dev-server等中间件代理接口访问nignx时,此时无浏览器参与,故没有同源限制,下面的跨域配置可不启用
add_header Access-Control-Allow-Origin http://www.domain1.com; #当前端只跨域不带cookie时,可为*
add_header Access-Control-Allow-Credentials true;
}
}

Comments