这几年经手的项目遇到过很多跨域的问题,是时候进行总结整理了。
跨域问题的本质
要解决跨域问题,很多时候更像是在做黑客,从浏览器被设计之初更多的是考虑到跨站点访问安全性,所以要禁止你跨域访问,请注意对于跨域问题重点针对的是JavaScript的安全限制,以防止来自第三方的恶意攻击。
我们首先来看下浏览器的同源策略:
同源:协议+端口+主域名+子域名相同
例如:
https://blog.csdn.net/weixin_40073115/article/details/103453116.html
https://blog.csdn.net/weixin_40073115/article/details/103425008.html
上面就是同源的这两个页面之间是针对一下操作
Cookie、LocalStorage 和 IndexDB 读取
DOM 和 JS 对象操作
到服务端的请求共通
除此之外:协议+端口+主域名+子域名 有任意不同的地方都被视为跨域
但从左到右浏览器根据同源策略所阻止行为的级别逐级降级,同时对于开发者要实现跨域的成本也逐级降级。另外如果访问的协议都不相同的情况不再本次跨域问题之列,例如https与http之间的跨域除了能通过iframe打开页面之外其他操作前端都无能为力。
我们下面所讲的解决跨域问题也是根据不同情形给予最佳的解决方案。
其原则是:最少的代码,性能上可靠,少依赖服务端,不带来新的安全隐患。
根据实际应用场景分跨域读取页面信息和跨域请求访问服务端数据两大类,第一类通用iframe解决如下
1.通过iframe
注意在不跨域的情况下网站设计上尽量不要使用iframe,w3c新版已经不推荐页面使用,事实上在不跨域情况下你也想不出来必须要用iframe完成的场景了,当然就跨域来说它的缺是一个很好的解决方案。
场景一:假定你需要在自己开发的页面中展示客户自己的某个系统画面,而这个系统对于你目前的系统而言不会有任何的访问交互可能,你仅仅能拿到的就是通过url可以访问到对方的画面。
此时属于:端口号主域名不同的情况(后面的是否相同已经没有意义)
此时你已别无选择除了iframe没有别的方案能够去解决它了,好在iframe可以满足你在自己页面中去打开它,还有虽然你无法去操作对方页面里的任何内容。
场景二,假设你想去拿到对方页面的数据或内容我们通过设置设置window.name的值可以实现只要浏览器窗口不关闭,无论中间调转了多少次链接,页面变成什么样子,都可以获得窗口下设置的window.name, 在浏览器环境中是一个全局window对象的属性,当在 iframe 中加载新页面时,name 的属性值依旧保持不变。
name 属性仅对相同域名的 iframe 可访问,所以我们必须在父页面中找到时机更改子页面的src 使其与父页面同源,再拿到window.name的值欺骗浏览器而拿到子页面的值
window.name 的特性:
1)每个窗口都有独立的window.name与之对应;
2)在一个窗口的生命周期中(被关闭前),窗口载入的所有页面同时共享一个window.name,每个页面对window.name都有读写的权限;
3)window.name一直存在与当前窗口,即使是有新的页面载入也不会改变window.name的值;
4)window.name可以存储不超过2M的数据,数据格式按需自定义。
window.name 的不足:
只适用于隐藏iframe的情形,没法将原页面内容展示,因为当子页面加载完成后需要更改其src,所以我们只能让他隐藏着。
关键代码:
父页面:
<script>
function load () {
var iframe = document.getElementById('iframe');
iframe.onload = function () {
var window = iframe .contentWindow;
console.log(window.name);
}
iframe.src = 'about:blank'; //让url地址改变,与当前页面同源,可以任意写,保持同源就好
}
</script>
<iframe id="iframe" src="https://xxx.github.io/xxx/" onload="load()"></iframe>
子页面中要做的事就是设置自己的window.name值把想暴露出来的信息给其赋值即可。
另外父页面也可以通过在url中插入hash值完成给子页面传值。
以上方法可完成子父业面间的通信。
场景三:假设你要访问的页面与你主域名相同子域名不同,此时我们通过降域处理来完成跨域。
通过设置window.domain将两个页面置为一样 document.domain = “domain.com”,即可像同源页面一样去进行相关操作。
通过postMesg方法也可以实现。
以上就是关于iframe跨域的相关总结。
如果要请求跨域网站的服务则需要一下几种解决方案
2.通过jsonp
浏览器设计上我们从页面元素:img、srcipt,link标签的src或href属性http://xxxxxxx"是不受限的,其原理就是“img标签的src和link标签的href会发送一个get请求去请求静态资源。浏览器对get请求的静态资源限制不受受同源限制的”
那么我们就可以发送get 请求到服务端去拿我们想要的数据了,对不起,虽然服务端会响应但返回的数据浏览器自己是没办法主动操作的,所以必须要服务端配合操作,用ajax举例:
$(function(){
$("a").on("click", function(){
$.ajax({
//type:"post",
url:"http://localhost:8080/html5/JsonServlet",
dataType:'jsonp',
jsonp:'mycallback',
jsonpCallback:'callback',
success:function(data) {
console.log(2222);
console.log(data.age);
}
});
})
});
服务端
public class AccessController {
@GetMapping(value="testJson")
public String testJson(String callback){
String message = "{a:1}";
String json = JSON.toJSONString(message);
String result = callback + "(" + json + ");";
return result;
}
核心就是后端必须配合前端去实现这个callback函数;
3.通过设置跨域请求头
认识CORS:全称"跨域资源共享"(Cross-origin resource sharing)。w3c支持的请求头属性。
目前几乎所有浏览器都支持CORS,IE则不能低于IE10。CORS的整个过程都由浏览器自动完成,前端无需做任何设置,跟平时发送ajax请求并无差异。所以实现CORS的关键在于服务器,只要服务器实现CORS接口,就可以实现跨域通信。
其请求方式使用下列方法之一:
GET
HEAD
POST
类别Content-Type 的值仅限于下列三者之一:
text/plain
multipart/form-data
我们用node端实现如下:
app.use(allowCrossDomain);
var express = require('express');
var app = express();
var allowCrossDomain = function (req, res, next) {
res.header('Access-Control-Allow-Origin', 'http://localhost:3001');
res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');
res.header('Access-Control-Allow-Headers', 'Content-Type');
next();
}
app.use(allowCrossDomain)
注意不要强暴使Access-Control-Allow-Origin 设为“*”,会严重破坏同源机制带来巨大风险。
4.h5 websocket解决跨域
Websocket是HTML5的一个持久化的协议,它实现了浏览器与服务器的全双工通信,同时也是跨域的一种解决方案。WebSocket和HTTP都是应用层协议,都基于 TCP 协议。但是 WebSocket 是一种双向通信协议,在建立连接之后,WebSocket 的 server 与 client 都能主动向对方发送或接收数据。同时,WebSocket 在建立连接时需要借助 HTTP 协议,连接建立好了之后 client 与 server 之间的双向通信就与 HTTP 无关了。
前提条件是服务端要实现与前端的socket链接。
5.node 中间代理
时至今日前后端分离的开发模式已经成为大趋势,特别是微服务架构下,node的出现无疑对前端的意义重大,当然对于跨域问题node也能解决
做法有很多种这里我们只简单说原理
页面通过固定的url接口访问node提供的服务,再由node服务端代理请求实际服务端,因为node端不受浏览器同源限制,所以你可以随意去跨站请求。
6 nginx代理跨域
与node一样, 同源策略是浏览器的安全策略,不是HTTP协议的一部分。服务器端调用HTTP接口只是使用HTTP协议,不会执行JS脚本,不需要同源策略,也就不存在跨越问题。
实现思路:通过nginx配置一个代理服务器(域名与主域相同,端口不同)做跳板机,反向代理访问目标服务接口,并且可以顺便修改cookie中domain信息,方便当前域cookie写入,实现跨域登录。
结束语
关于跨域问题前端目前就这6种解决方案了,当然对于代理模式还有其他的,但原理和上面说的一样。
原创不易,望君点赞!
愿分享精神,永垂不朽!
来源:CSDN
作者:guocongcong-cc
链接:https://blog.csdn.net/weixin_40073115/article/details/103474281