常用跨域方法总结

常用跨域方法总结

为什么要跨域?

因为浏览器的一种安全机制——同源策略的限制,导致不能直接获取不同源的资源,所以要跨域。

同源策略限制了从同一个源加载的文档或脚本如何与来自另一个源的资源进行交互。这是一个用于隔离潜在恶意文件的重要安全机制。

限制之一是不能通过ajax的方法去请求不同源中的文档。
第二个限制是浏览器中不同域的框架(iframe)之间是不能进行js的交互操作的。

那么什么才叫“同源”呢?

  • 协议相同
  • 域名相同
  • 端口号相同

clipboard.png

图来自MDN,参见最后Reference.

下面介绍常用的几种跨域方法。

jsonp 跨域

原理:

  • 利用了<script>标签不受浏览器同源限制的影响,
  • 因为借用<script>发起的请求,所以会把请求到的内容当作js代码来解析执行。

缺点:只能发送get请求
下面是一个简单的例子

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<script>
function callback (msg) {
  console.log('json:', msg)
}
</script>
<script src="http://localhost:8080?cb=callback ">
</script>
</body>
</html>

如上,定义了一个全局的callback 函数,然后利用<script>来发起get请求,注意把函数名称callback 一同发送给服务端,为什么呢,接着往下看服务端代码

//nodeJs
var http = require('http');
var url = require('url');

/**
 * 客户端请求的函数
 * @param req
 * @param res
 */
function onRequest (req, res) {
  console.log("获取到的请求参数的路径:" + req.url);

  //得到键值对
  var arg = url.parse(req.url, true).query;
  //打印键值对中的值
  console.log(arg.cb);

  res.writeHead(200);
  res.write(arg.cb + '("我今天要用jsonp来跨域获取数据")');
  res.end();
}

http.createServer(onRequest).listen(8080);

服务端简单解析cb参数(我们传的函数名称),然后返回一段字符串callback("我今天要用jsonp来跨域获取数据")
浏览器会收到响应如下:

clipboard.png

clipboard.png

发现什么了吗?响应内容callback("我今天要用jsonp来跨域获取数据")会被当作js代码来执行,正好调用了我们之前定义的callback函数。
由此,我们成功的利用jsonp通过跨域获取到了想要的数据。

document.domain跨域(子域名不同的框架之间)

开头我们说到不同源的框架之间是不能进行js交互操作的,其实是可以获取window对象,但不能获取window的属性。
原理:

  • document.domain的值是可以设置的,但仅限于设置为自身或是更高一级的父级域名(主域名相同)。

那么主域名相同,子域名不同的框架之间跨域获取数据的思路就来了,我们把它们的document.domain都设置成主域名不就完事了?
比如有一个页面a.google.com/1.html
这里参考了其他文章的代码。出处见本文最下方

<iframe id = "iframe" src="http://b.google.com/2.html" onload = "test()"></iframe>
<script type="text/javascript">
    document.domain = 'google.com';//设置成主域
    function test(){
        alert(document.getElementById('iframe').contentWindow);//contentWindow 可取得子窗口的 window 对象
    }
</script>

另一个页面b.google.com/2.html只需设置主域名为google.com,两个框架间就可以进行交互啦。

<script type="text/javascript">
document.domain = 'google.com';//设置成主域
</script>

location.hash跨域(不同源的框架之间)

原理:

  • hash字段(经常用于锚点定位)不属于http协议的部分,请求不会携带hash信息,所以改变不会重新请求资源(但是会产生新的浏览器历史记录,许多前端路由也是借用这个原理实现的)
  • 父窗口可以对iframe进行URL读写,iframe也可以读写父窗口的URL(不同源的话,IE、Chrome不允许修改parent.location.hash的值,但我们仍有处理方式)

思路:

  • 如果是父窗口向子窗口跨域传递数据,直接修改子窗口url的hash就可以了,比较简单这里就不贴代码了。
  • 子窗口向父亲窗口跨域传递数据就需要多加一个代理窗口(因为IE,Chrome),这个代理窗口和父亲窗口同源就可以了,在这个代理窗口改变父亲窗口的hash,父亲窗口监听hashchange就可以了。

代码如下:
父亲窗口页面
http://localhost:63342/test-field/cross-origin/local-test/hash-parent.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<iframe src="http://blank121.github.io/test-field/cross-origin/hash.html">
</iframe>
<script>
console.log('old hash:', location.hash)
window.addEventListener('hashchange', function (ev) {
  console.log('new hash:', location.hash)
})
</script>
</body>
</html>

子窗口页面
http://blank121.github.io/tes...

try{
  parent.location.hash='今天我要用hash跨域'
  //chrome ie 直接修改parent.location.hash报错
}catch (e){
  var iframe = document.createElement('iframe')
  iframe.src = 'https://localhost:63342/test-field/cross-origin/local-test/hash-proxy.html'+'#今天我要用hash跨域'
  document.body.appendChild(iframe);
}

代理窗口页面
http://localhost:63342/test-field/cross-origin/local-test/hash-proxy.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<script>
console.log('proxy',location.hash)
parent.parent.location.hash = location.hash
</script>
</body>
</html>

如上,在子窗口页面内修改了代理窗口的hash值,代理窗口又修改了父亲窗口的hash值,父亲窗口监听hashchange就可以获取到不同源的子窗口传来的数据啦。
控制台结果如下:

clipboard.png

window.name跨域(不同源框架间)

原理:

window对象有个name属性,该属性有个特征:即在一个窗口(window)的生命周期内,窗口载入的所有的页面都是共享一个window.name的,每个页面对window.name都有读写的权限,window.name是持久存在一个窗口载入过的所有页面中的,并不会因新页面的载入而进行重置。

利用一个窗口的生命周期内,载入不同页面window.name不变的特性
贴代码前首先去做个有趣的实验
在必应的首页设置一下window.name

clipboard.png

然后输入location.href = 'http://www.baidu.com',跳转到百度后再看一下name

clipboard.png

666,window.name果然没骗我,没有变化。

接下来先贴代码
父亲窗口:
http://localhost:63342/test-field/cross-origin/local-test/name-parent.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<iframe id="frame" src="https://blank121.github.io/test-field/cross-origin/name.html"></iframe>
<script>
var iframe = document.getElementById('frame')
var loaded = false
iframe.onload = function () {
  if (!loaded) {
    loaded = true
    iframe.src = 'http://localhost:63342/test-field/cross-origin/local-test/name-proxy.html'
  } else {
    console.log(iframe.contentWindow.name)
  }
}
</script>
</body>
</html>

子窗口:
https://blank121.github.io/te...

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>window.name跨域</title>
</head>
<body>
<script>
window.name='今天,我要用window.name来实现跨域'
</script>
</body>
</html>

代理窗口(啥也没做。主要是要和父亲窗口同源来传递name)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
</body>
</html>

代码如上,主要思路就是利用window.name不变性,子窗口设置了name之后,来加载一个和父亲窗口同源的代理窗口,以此来获取name(注意子窗口和代理窗口是同一个iframe,加载不同页面而已,所以window.name不变)

clipboard.png

postMessage跨域

首先了解一下postMessage
otherWindow.postMessage(message, targetOrigin);
otherWindow:指目标窗口,也就是给哪个window发消息,是 window.frames 属性的成员或者由 window.open 方法创建的窗口
message: 是要发送的消息,类型为 String、Object (IE8、9 不支持)
targetOrigin: 是限定消息接收范围,不限制请使用 *

postMessage是HTML5新特性,跨域简直太方便了,就是兼容性要注意一下。

发送方页面

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<iframe id="frame" src="https://blank121.github.io/test-field/cross-origin/receive.html">
</iframe>
<script>
var iframe = document.getElementById('frame')
iframe.onload = function () {
    iframe.contentWindow.postMessage("我今天就是要用postMessage跨域","https://blank121.github.io")
}
</script>
</body>
</html>

接收方页面

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<script>
function receiveMessage(event)
{
  var p = document.createElement('p')
  p.innerText = event.data
  document.body.appendChild(p)
}
window.addEventListener("message", receiveMessage, false);
</script>
</body>
</html>

接收方监听一下message事件,就可以接收到信息啦。

clipboard.png

WebSocket跨域

WebSocket 是一种在客户端与服务器之间保持TCP长连接的网络协议,这样它们就可以随时进行信息交换(双工通讯)。
虽然任何客户端或服务器上的应用都可以使用WebSocket,但原则上还是指浏览器与服务器之间使用。通过WebSocket,服务器可以直接向客户端发送数据,而无须客户端周期性的请求服务器,以动态更新数据内容。

WebSocket 非常强大,笔者在这方面也是小白级别的,以后有时间会详细研究学习。

跨域代码如下
页面:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
</body>
</html>
<script>
// Create WebSocket connection.
const socket = new WebSocket('ws://127.0.0.1:8080');

// Connection opened
socket.addEventListener('open', function (event) {
  socket.send('Hello Server!');
});

// Listen for messages
socket.addEventListener('message', function (event) {
  console.log('Message from server', event.data);
});
</script>

服务端nodeJs代码:

var WebSocketServer = require('ws').Server;
var wss = new WebSocketServer({ port: 8080 });

wss.on('connection', function connection(ws) {
  ws.on('message', function incoming(message) {
    console.log('received: %s', message);
     ws.send("hello"+message);
  });
});

结果如图:
浏览器端:

clipboard.png

服务端:

clipboard.png

完美实现了跨域。

CORS(cross origin resource sharing 最常用)

老生常谈的CORS,优秀的文章已经非常多了,大家可以搜一下,非常重要,有机会我会专门写一篇文章来学习总结,在此就不再详述了

最后

不得不说,这些方法还是比较巧妙的,在此写下一篇文章来总结一下,感觉自己面对跨域丝毫不慌啦。

Reference

前端跨域整理
正确面对跨域,别慌
浏览器的同源策略

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 在 uni-app 中,解决跨域请求的方法有以下几种: 1. 使用 jsonp 请求 2. 使用代理,在本地启动一个服务器来作为代理,发起跨域请求时以本地服务器为中介,避免跨域的限制 3. 在服务器端配置跨域,如果服务器端允许跨域,则可以在服务器端设置 HTTP header,允许跨域访问。例如,在 Express.js 中可以使用 cors 中间件来实现。 如果你想深入了解跨域请求的原理,你可以参考以下资料: - 跨域资源共享 (CORS):https://developer.mozilla.org/zh-CN/docs/Web/HTTP/CORS - HTTP 访问控制 (CORS):https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Access_control_CORS - 跨域请求的简单请求与预检请求:https://developer.mozilla.org/zh-CN/docs/Web/HTTP/CORS/Simple_requests_and_preflight_requests ### 回答2: 在uni-app中请求跨域解决方法有以下几种: 1. 使用uni.request方法发送请求时,在请求header中添加"Access-Control-Allow-Origin"字段,允许指定的域名或通配符"*",例如: ``` uni.request({ url: 'http://example.com/api', method: 'GET', header: { "Access-Control-Allow-Origin": "*" }, success: function(res) { console.log(res.data); } }); ``` 2. 在uni-app的开发配置文件manifest.json中,添加"networkTimeout"字段来配置跨域请求的超时时间,例如: ``` "networkTimeout": { "request": 10000, "connectSocket": 10000, "uploadFile": 10000, "downloadFile": 10000 } ``` 3. 使用uni.request方法发送请求时,设置withCredentials为true,开启跨域请求携带cookie功能。但需要注意服务器端也需要设置响应头允许接收cookie,例如: ``` uni.request({ url: 'http://example.com/api', method: 'GET', withCredentials: true, success: function(res) { console.log(res.data); } }); ``` 4. 在uni-app的开发配置文件manifest.json中,添加"filters"字段配置全局过滤器来处理跨域请求,例如: ``` "filters": { "request": { "origin": "*", "x-requested-with": "*", "content-type": "application/json" } } ``` 以上是一些常用的uni-app中处理cors跨域请求的方法,根据具体情况选择适合的解决方案。 ### 回答3: 在UniApp中解决跨域问题可以通过配置服务器设置CORS(跨域资源共享)来实现。 首先,在后端服务器上设置CORS。在服务器返回响应时,需要在响应头中添加相关的CORS头信息,允许前端跨域访问该接口。常见的CORS头信息包括:Access-Control-Allow-Origin、Access-Control-Allow-Headers、Access-Control-Allow-Methods等。具体配置方法可以根据后端服务器的不同而有所差异,请根据服务器文档进行设置。 其次,在UniApp中发起请求时,需要在请求头中添加Origin字段表示请求的源地址。同时,UniApp默认会在请求头中自动添加Referer字段来表示访问来源,可根据需要进行调整。 另外,UniApp还提供了特定的API来设置请求的配置,可以通过修改uni.request方法的header参数或通过配置uni.request.defaults.header来添加请求头,以适应不同的CORS设置。 需要注意的是,前端涉及到跨域请求时,在接口上必须进行预检请求(Preflight Request)的检测,包括发送一个OPTIONS请求以校验服务器是否允许当前请求。 总结起来,解决UniApp中的CORS跨域问题,需要在后端服务器进行相关CORS设置,并在UniApp中添加请求头信息,确保请求能够正常跨域访问后端接口。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值