Skip to content

跨域

跨域是指在同一页面中引入来自不同源的资源时,由于浏览器的同源策略,导致无法正常加载资源或访问资源的问题。

常见的跨域场景包括:

  • 在 Web 页面中通过 AJAX 请求获取来自不同域的数据;
  • 在 Web 页面中通过 iframe 标签引入来自不同域的页面;
  • 在 Web 页面中通过 script 标签引入来自不同域的 JavaScript 文件;

同源策略

同源策略(Same-Origin Policy)是浏览器一种安全策略,同源指的是:协议、域名、端口号必须一致。

同源策略限制了一个网页中的 JavaScript 代码只能读取同源网页的数据。如果两个页面的协议、域名或端口有任何一个不同,都被视为不同源,那么就会存在跨域问题。

同源策略是浏览器最基本的安全机制之一,可以防止恶意网站通过 iframe、XHR 等方式窃取用户的信息或者进行 CSRF 攻击等。同时,也为开发者提供了一种简单、可靠的方式来保证自己的 Web 应用程序不受到恶意攻击的威胁。

解决跨域

  • JSONP:利用 script 标签可以跨域的特性,将要获取的数据作为参数传递给一个回调函数,服务器将数据作为函数调用的参数返回。缺点是只支持 GET 请求,并且存在安全问题。
  • CORS:跨域资源共享是一种标准的跨域解决方案,需要服务器设置相关的响应头,可以支持 GET、POST 等各种类型的请求,并且安全性更高。
  • 跨域代理:在同源页面中通过 AJAX 请求获取数据时,可以将请求发送到同源的后端服务器,由后端服务器再将请求发送到目标服务器获取数据,并将数据返回给前端页面。这种方式可以解决跨域问题,但需要部署额外的后端服务。

JSONP (JSON with Padding)

html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>JSONP 示例</title>
  </head>
  <body>
    <div id="result"></div>
    <script>
      function jsonpCallback(data) {
        document.getElementById('result').innerHTML = data.name
      }
    </script>
    <script src="https://example.com/api/getUser?callback=jsonpCallback"></script>
  </body>
</html>

在这个示例中,页面向 https://example.com/api/getUser 发送了一个跨域请求,并使用名为 jsonpCallback 的回调函数来接收响应数据。当服务器返回数据时,它会使用回调函数名将数据包裹在一个 JavaScript 函数中,例如:

js
jsonpCallback({ name: '张三' })

当客户端代码接收到这个响应时,它会直接调用 jsonpCallback 函数,并将响应数据传递给它。页面中的 result 元素就会显示 "张三"。

JSONP 虽然简单易用,但它有几个明显的缺点:

  • 只支持 GET 请求:由于 JSONP 是通过创建一个 <script> 标签来发送请求的,因此它只支持 GET 请求,不能像 AJAX 那样支持 POST、PUT、DELETE 等其他类型的请求。
  • 安全风险:JSONP 允许任何服务器返回的 JavaScript 代码在客户端执行,因此存在安全风险。如果不谨慎使用,可能会导致 XSS 攻击等安全问题。
  • 缓存问题:由于 JSONP 请求是通过创建一个 <script> 标签来发送的,因此浏览器会缓存请求的结果,这可能会导致无法及时获取最新的数据。

CORS (Cross-Origin Resource Sharing)

CORS (Cross-Origin Resource Sharing) 是一种浏览器安全机制,用于在跨域请求中控制资源的访问权限。

在同源策略的限制下,浏览器只允许在相同域名、协议和端口下进行 HTTP 请求,如果请求的资源不在当前页面所在的域名下,则会被拒绝。而 CORS 允许服务器在响应中设置一些 HTTP 头部信息,告诉浏览器该如何处理跨域请求。通过这些头部信息,服务器可以控制允许哪些域名、协议和端口访问资源,从而解决跨域请求的问题。

在使用 CORS 时,需要在服务器端设置响应头部信息。常见的响应头部信息包括:

  • Access-Control-Allow-Origin:指定允许访问资源的域名,可以设置为 "*" 表示允许任何域名访问。
  • Access-Control-Allow-Methods:指定允许访问资源的 HTTP 方法,例如 GET、POST、PUT 等。
  • Access-Control-Allow-Headers:指定允许访问资源的请求头部信息,例如 Authorization、Content-Type 等。
  • Access-Control-Expose-Headers:指定哪些 HTTP 头的名称浏览器能在访问;
  • Access-Control-Max-Age:指定预检请求的有效期,即在此时间段内不需要发送预检请求。

通过设置这些头部信息,服务器可以告诉浏览器允许哪些跨域请求,从而保证资源的安全访问。

其中,在进行一些特定类型的跨域请求时,浏览器会在发送真正的请求之前,先发送一个预检请求,用于询问服务器是否支持跨域请求,这个预检请求使用的是 OPTIONS 方法,称为预检请求(Preflight Request)。

预检请求会包含一些额外的头部信息,例如 Origin、Access-Control-Request-Method、Access-Control-Request-Headers 等,用于询问服务器是否支持该跨域请求,并获取一些额外的信息。如果服务器不支持该跨域请求,则会返回一个错误响应,浏览器会根据错误信息进行处理。

  • Origin:获取资源的请求是从什么域发起的;
  • Access-Control-Request-Headers:用于发起一个预请求,告知服务器正式请求会使用那些 HTTP 头;
  • Access-Control-Request-Method:用于发起一个预请求,告知服务器正式请求会使用哪一种 HTTP 请求方法;

代理跨域

代理跨域是一种常见的跨域解决方案,它通过在服务器端设置代理来实现跨域请求。代理跨域的基本思路是,通过服务器端转发请求,将跨域请求转换为同域请求,从而避免了浏览器的跨域限制。

常见代理跨域有:

  • 反向代理:反向代理是指代理服务器将请求转发给目标服务器,并将响应返回给客户端。比如:生产当中 Nginx 反向代理。反向代理可以实现负载均衡、安全过滤、静态资源缓存等功能。
  • 正向代理:正向代理是指代理服务器将客户端的请求转发给目标服务器,并将响应返回给客户端。比如:开发时 webpack-dev-server 正向代理。 正向代理可以实现隐藏客户端 IP、加速访问速度、访问控制等功能。