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)
|
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'); 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); 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
| window.onmessage = function(e) { console.log(e.data) 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> 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
| const WebSocket = require('ws');
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
| const http = require('http')
const server = http.createServer((request, response) => { 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
| server { listen 81; server_name www.domain1.com; location / { proxy_pass http: 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: add_header Access-Control-Allow-Credentials true; } }
|