跨域

227 阅读4分钟

什么是跨域

跨域是指从一个域的网页去请求另一个域的资源。只要协议,域名,端口有任何一个的不同,就被当作跨域。

js跨域是指通过js在不同的域之间进行数据传输或通信。比如用ajax向一个不同的域请求数据,或者通过js获取页面中不同域的iframe中的数据。

跨域并不是请求发不出去,请求能发出去,服务端能收到请求并正常返回结果,只是结果被浏览器拦截了。

iframe

利用iframe标签,我们可以在自己的网站页面加载别人网站或者本站其他页面的内容。

src规定了在iframe中显示的文档的url。scrolling规定是否显示页面滚动条。

为什么浏览器要限制跨域访问呢

如果一个网页可以随意的访问另外一个网站的资源,浏览器很容易受到XSS,CSRF等攻击。

浏览器出于安全考虑,有了同源策略,限制跨域访问。

跨域的方法

跨域资源共享(CORS)

一般只要服务端实现CORS接口,就可以跨域。

简单请求

1. 普通跨域请求。只要服务器端响应头设置Access-Control-Allow-Origin,表明哪些域可以访问资源,如果设置通配符则表示所有网站可以访问资源。如果浏览器检测到服务端相应的设置,就可以允许Ajax进行跨域的访问。

2. 如果是带cookie请求的跨域,在客户端也要进行设置。在ajax方法中加上

并且要在服务器响应头中的Access-Control-Allow-Origin指定发送请求的域名,不能用 * 。这样可以实现一个需要登录到另一个系统才可以看见页面的跨域(带cookie的跨域)。

复杂请求:先发送OPTIONS预检请求

对那些可能对服务器数据产生副作用的HTTP请求方法(特别是GET以外的HTTP请求),浏览器必须首先使用OPTIONS方法发起一个预检请求,从而获知服务端是否允许该跨域请求。

浏览器先询问服务器,当前网页所在域名是否在服务器的许可名单之中,以及可以使用哪些HTTP动词和头信息字段。只有得到肯定答复,浏览器才会发出正式的XMLHttpRequest请求,否则就报错。

预检请求的请求方法是OPTION。头信息包括,Access-Control-Request-Method,Access-Control-Request-Headers。

预检请求的回应:Access-Control-Allow-Origin,Access-Control-Allow-Headers,Access-Control-Allow-Credentials(是否需要携带身份凭证Cookies),Access-Control-Max-Age。

服务器通过预检请求以后,浏览器开始发送正常的CORS请求,但要加上Origin头信息字段(浏览器的域);服务器的回应也要包含Access-Control-Allow-Origin头信息字段

JSONP

JSONP原理

利用 script 标签没有跨域限制的漏洞,网页可以得到从其他来源动态产生的JSON数据。JSONP请求一定需要对方的服务器做支持才可以。  

JSONP的实现流程

  1. 声明一个回调函数,其参数名(如show)当作参数值,要传递给跨域请求数据的服务器,函数形参为 要从服务器获取的目标数据

  2. 创建一个 script 标签,把那个跨域的API数据接口地址,赋值给script的src,还要在这个地址中向服务器传递该函数名(可以通过问号传参 ?callback=show )

  3. 服务器接收到请求后,需要进行特殊的处理:把传递进来的函数名和它需要给你的数据拼接成一个字符串。例如:传递进去的函数名是show,它准备好的数据是 show('数据')

  4. 最后服务器把准备的数据通过HTTP协议返回给客户端,客户端再调用执行之前声明的回调函数(show),对返回的数据进行操作

手写JSONP

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
</head>

<body>

  <script>
    /**
     * 手写jsonp并返回Promise对象
     * 参数url,data:json对象,callback函数
     */
    function jsonp(url, data = {}, callback = 'callback') {
      // 处理json对象,拼接url
      data.callback = callback
      let params = []
      for (let key in data) {
        params.push(key + '=' + data[key])
      }
      console.log(params.join('&'))
      // 创建script元素
      let script = document.createElement('script')
      script.src = url + '?' + params.join('&')
      document.body.appendChild(script)
      // 返回promise
      return new Promise((resolve, reject) => {
        window[callback] = (data) => {
          try {
            resolve(data)
          } catch (e) {
            reject(e)
          } finally {
            // 移除script元素
            script.parentNode.removeChild(script)
            console.log(script)
          }
        }
      })

    }
    jsonp('http://photo.sina.cn/aj/index', {
      page: 1,
      cate: 'recommend'
    }, 'jsoncallback').then(data => {
      console.log(data)
    })
  </script>
</body>

</html>

CORS和JSONP对比

1. JSONP只能实现GET请求,而CORS支持所有类型的HTTP请求。

2. 使用CORS,开发者可以使用普通的XMLHttpRequest发起请求和获得数据,比起JSONP有更好的错误处理。

3. JSONP主要被老的浏览器支持,它们往往不支持CORS;而绝大多数现代浏览器都已经支持了CORS。