跳转到内容

浏览器跨域访问解决方案

浏览器跨域访问解决方案

一、什么是跨域?

所谓跨域是指一个域下的文档或脚本试图去请求另一个域下的资源。

跨域的严格一点的定义是:

  • 当协议、子域名、主域名、端口号中任意一个不相同时,都算作不同域
  • 不同域之间相互请求资源,就算作"跨域"

比如:

  1. http://www.example.com/index.html 调用 http://www.example.com/server.php —— 同域
  2. http://www.example.com/index.html 调用 http://www.example2.com/server.php —— 跨域(主域名不同)
  3. http://www.example.com/index.html 调用 http://sub.example.com/server.php —— 跨域(子域名不同)
  4. http://www.example.com:8080/index.html 调用 http://www.example.com/server.php —— 跨域(端口不同)
  5. http://www.example.com/index.html 调用 https://www.example.com/server.php —— 跨域(协议不同)

二、为什么会有跨域限制?

在浏览器中有一个安全功能叫做「同源策略」,它能够阻止一个域在浏览器中加载或运行另一个域的脚本。同源策略的目的是为了防止恶意网站窃取用户隐私或执行非法操作。

三、解决方法

1. CORS (跨源资源共享)

跨域资源共享(CORS) 是一种机制,它使用额外的 HTTP 头来告诉浏览器,让运行在一个域上的Web应用被准许访问来自不同源服务器上的指定的资源。

CORS需要浏览器和服务器同时支持。目前,所有浏览器都支持该功能,IE浏览器不能低于IE10。

代码示例

前端代码:

javascript
// 使用XMLHttpRequest对象
let xhr = new XMLHttpRequest()
xhr.open('GET', 'http://haorooms.com/data', true)
xhr.onreadystatechange = function () {
  if (xhr.readyState === 4 && xhr.status === 200) {
    console.log(xhr.responseText)
  }
}
xhr.send(null)

// 使用jQuery
$.ajax({
  url: 'http://haorooms.com/data',
  type: 'GET',
  success(data) {
    console.log(data)
  }
})

服务器端设置:

在服务器端,需要设置CORS响应头,主要是Access-Control-Allow-Origin

Apache服务器配置:

Header set Access-Control-Allow-Origin "*"

PHP服务器配置:

php
<?php
header("Access-Control-Allow-Origin: *");
header("Access-Control-Allow-Methods: GET, POST");
header("Access-Control-Allow-Headers: X-Requested-With");
?>

Node.js服务器配置:

javascript
res.setHeader('Access-Control-Allow-Origin', '*')
res.setHeader('Access-Control-Allow-Methods', 'GET, POST')
res.setHeader('Access-Control-Allow-Headers', 'X-Requested-With')

2. JSONP (JSON with Padding)

JSONP的原理是利用<script>标签可以跨域访问资源的特性,通过动态创建script标签来完成对数据的访问。

代码示例

前端代码:

javascript
function dosomething(data) {
  console.log(data)
}

let script = document.createElement('script')
script.src = 'http://haorooms.com/data.php?callback=dosomething'
document.body.appendChild(script)

简化版:

javascript
<script src="http://haorooms.com/data.php?callback=dosomething"></script>

服务器端返回:

php
<?php
$callback = $_GET['callback'];
$data = array('name' => 'John', 'age' => 30);
echo $callback . '(' . json_encode($data) . ')';
?>

服务器返回的数据格式如下:

javascript
dosomething({ name: 'John', age: 30 })

3. document.domain + iframe

该方式仅限主域相同,子域不同的跨域应用场景。

代码示例

www.example.com/a.html中:

html
<iframe id="iframe" src="http://sub.example.com/b.html" onload="test()"></iframe>
<script>
    document.domain = 'example.com';
    function test() {
        console.log(document.getElementById('iframe').contentWindow.data);
    }
</script>

sub.example.com/b.html中:

javascript
<script>
  document.domain = 'example.com';
  var data = 'Hello World!';
</script>

4. window.name + iframe

window.name属性的独特之处:name值在不同的页面(甚至不同域名)加载后依旧存在。

代码示例

a.html中:

javascript
let iframe = document.createElement('iframe')
iframe.style.display = 'none'
iframe.src = 'http://example.com/b.html'
document.body.appendChild(iframe)

iframe.onload = function () {
  iframe.onload = function () {
    let data = iframe.contentWindow.name
    console.log(data)
    iframe.parentNode.removeChild(iframe)
  }
  iframe.src = 'about:blank'
}

b.html中:

javascript
window.name = '要传输的数据'

5. location.hash + iframe

此方案和window.name类似,区别在于,它通过修改url的hash值来传递数据。

6. postMessage

HTML5引入了window.postMessage,这个方法可以安全地实现跨源通信。

代码示例

a.html中:

javascript
let iframe = document.getElementById('iframe')
iframe.contentWindow.postMessage('Hello World!', 'http://example.com')
window.addEventListener('message', (e) => {
  if (e.origin !== 'http://example.com')
    return
  console.log(e.data)
}, false)

b.html中:

javascript
window.addEventListener('message', (e) => {
  if (e.origin !== 'http://a.com')
    return
  console.log(e.data)
  e.source.postMessage('Got it!', e.origin)
}, false)

7. 代理(Proxy)服务器

跨域限制是浏览器行为,但服务器之间通信并不受同源策略的限制。因此,可以使用服务器作为代理,将请求发送到目标服务器,然后将响应返回给客户端。

以上就是常见的几种跨域访问解决方案,具体选择哪种方式取决于你的场景需求和环境限制。在现代Web应用中,CORS是最为推荐的跨域解决方案。