跨域解决方案-jsonp/CORS/postMessage/hash/WebSocket/proxy
什么是跨域
浏览器会有同源策略,域名,协议,端口只要有一个不同就是跨域.
同源策略只限制浏览器端,跨域请求是可以发去的,但是请求响应response被浏览器堵塞了,是限制了不同源的读,但不限制不同源的写,服务端没有同源策略这一限制,form表单可以跨域发送信息也是这个原因。
限制:
(1) Cookie、LocalStorage 和 IndexDB 无法读取。
(2) DOM 无法获得。
(3) AJAX 请求不能发送
浏览器某些静态文件加载标签不受同源策略限制:<script>< link>< img><video><audio><iframe>
解决跨域的方法
jsonp
- 原理:客户端告诉服务一个回调函数的名称,服务器在返回的scritp里面调用这个回调函数,同时传进客户端需要的数据,这样返回的代码就在浏览器执行了
优缺点:
- 常用方法,简单适用,老式浏览器全部支持,服务器改造非常小
- 只能实现get一种请求,不安全 容易遭到xss攻击
// 前端页面 function writeDate(_date){ document.write(_date); } <script src='http://192.168.0.103:9000/getDate?callback=writeDate'></script> // 服务端返回一个脚本,在这个脚本里面执行writeDate函数: function getDate(response, callback){ response.writeHead(200, {"Content-Type": "text/javascript"}); var data = "2016-2-19"; response.end(callback + "('" + data + "')"); } //jQuery $.ajax({ url: 'http://192.168.0.103:9000/getDate', type: 'get', dataType: 'jsonp', // 请求方式为jsonp jsonpCallback: "handleCallback", // 自定义回调函数名 data: {} }); //Vue this.$http.jsonp('http://192.168.0.103:9000/getDate', { params: {}, jsonp: 'handleCallback' }).then((res) => { console.log(res); })
CORS跨域资源共享
跨域资源共享(CORS) 是一种机制,它使用额外的 HTTP 头来告诉浏览器 让运行在一个 origin (domain) 上的Web应用被准许访问来自不同源服务器上的指定的资源。 ----MDN(CORS)
cors将跨域请求分为简单请求和非简单请求
简单请求
(1) 请求方法是以下三种方法之一:HEAD/GET/POST
(2)HTTP的头信息不超出以下几种字段:Accept/Accept-Language/Content-Language/Last-Event-ID/
Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain
实现方法
服务端在http响应头中添加Access-Control-Allow-origin
“*”表示任何源都可以访问,也可以指定可以访问的源
非简单请求/需预检的请求
必须首先使用 OPTIONS
方法发起一个预检请求到服务器,返回码是204,预检测通过才会真正发出请求,这才返回200。
触发非简单请求的条件
- 使用了下面任一 HTTP 方法满足非简单请求:put/delete/connect/options/trace/patch
- 人为设置了对 CORS 安全的首部字段集合之外的其他首部字段
postMessage
window.postMessage(message,targetOrigin) 方法是html5新引进的特性,不受同源策略限制。
用于解决以下方面的问题:
a.) 页面和其打开的新窗口的数据传递
b.) 多窗口之间消息传递
c.) 页面与嵌套的iframe消息传递
d.) 上面三个场景的跨域数据传递
目前IE8+、FireFox、Chrome、Opera等浏览器都已经支持window.postMessage方法。
// postMessage // 窗口A(http:A.com)向跨域的窗口B(http:B.com)发送信息 Bwindow.postMessage('data', 'http://B.com'); // 在窗口B中监听 Awindow.addEventListener('message', function (event) { console.log(event.origin); // 发送源,接受谁 console.log(event.source); // Awindow的引用 console.log(event.data); }, false);
安全问题
- 如果不希望从其他网站接收message,不要为message事件添加任何事件侦听器。
- 始终使用origin和source属性验证发件人的身份。
- 始终指定精确的目标origin,而不是*。 恶意网站可以在不知情的情况下更改窗口的位置,因此它可以拦截使用postMessage发送的数据。
hash
hash为url#后的内容,hash改变页面不刷新, 故而可以实现跨域通信
?后的search内容 ,也叫query 改变页面刷新,不能做跨域通信
// 利用hash,场景是当前页面 A 通过iframe或frame嵌入了跨域的页面 B // 在A中伪代码如下: var B = document.getElementsByTagName('iframe'); B.src = B.src + '#' + 'data'; // 在B中的伪代码如下 window.onhashchange = function () { var data = window.location.hash; };
WebSocket
http://www.ruanyifeng.com/blo...
HTTP 协议有一个缺陷:通信只能由客户端发起,做不到服务器主动向客户端推送信息。如果服务器有连续的状态变化,客户端要获知就非常麻烦。我们只能使用"轮询":每隔一段时候,就发出一个询问,了解服务器有没有新的信息。最典型的场景就是聊天室。
WebSocket 最大特点就是,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息
var ws = new WebSocket('wss://echo.websocket.org'); // 发送 ws.onopen = function (evt) { console.log('Connection open ...'); ws.send('Hello WebSockets!'); }; // 接受 ws.onmessage = function (evt) { console.log('Received Message: ', evt.data); ws.close(); }; // 关闭 ws.onclose = function (evt) { console.log('Connection closed.'); };
vue-cli 3.0 本地环境proxy代理跨域
https://cli.vuejs.org/zh/conf...
如果你的前端应用和后端 API 服务器没有运行在同一个主机上,你需要在开发环境下将 API 请求代理到 API 服务器。这个问题可以通过 vue.config.js
中的 devServer.proxy
选项来配置。
devServer.proxy
可以是一个指向开发环境 API 服务器的字符串:
module.exports = { devServer: { proxy: 'http://localhost:4000' } }
这会告诉开发服务器将任何未知请求 (没有匹配到静态文件的请求) 代理到http://localhost:4000
。
如果你想要更多的代理控制行为,也可以使用一个 path: options
成对的对象。完整的选项可以查阅 http-proxy-middleware 。
module.exports = { devServer: { proxy: { '/api': { target: '<url>', ws: true, changeOrigin: true }, '/foo': { target: '<other_url>' } } } }
参考:
不要再问我跨域的问题了
前端常见跨域解决方案(全)