浏览器的同源策略

同源策略是一个重要的安全策略,它用于限制一个origin的文档或者它加载的脚本如何能与另一个源的资源进行交互。它能帮助阻隔恶意文档,减少可能被攻击的媒介。所谓的同源是指协议、域名和端口号都相同,如果有一个不同都不算是同源。

浏览器限制脚本内发起的跨源 HTTP 请求。 例如,XMLHttpRequest和 Fetch API遵循同源策略。这意味着使用这些 API 的 Web 应用程序只能从加载应用程序的同一个域请求 HTTP 资源,假如发送一个跨域请求,浏览器则会将响应结果丢弃,除非其符合CORS策略。

跨域方案

本地服务器代理

这是开发环境中最常用的一种跨域方法,在webpack或vite中进行相应的配置,然后由nodejs开发服务器代理请求转发给目标服务器,由于同源策略只在浏览器中起作用,从而绕开了同源策略的限制,开发服务器得到响应后再返回给浏览器。

// 以vite为例
export default defineConfig({
  server: {
    proxy: {
      // 字符串简写写法
      '/foo': '<http://localhost:4567>',
      // 选项写法
      '/api': {
        target: '<http://jsonplaceholder.typicode.com>',
        changeOrigin: true,
        rewrite: (path) => path.replace(/^\\/api/, '')
      },
      // 正则表达式写法
      '^/fallback/.*': {
        target: '<http://jsonplaceholder.typicode.com>',
        changeOrigin: true,
        rewrite: (path) => path.replace(/^\\/fallback/, '')
      },
      // 使用 proxy 实例
      '/api': {
        target: '<http://jsonplaceholder.typicode.com>',
        changeOrigin: true,
        configure: (proxy, options) => {
          // proxy 是 'http-proxy' 的实例
        }
      }
    }
  }
})

反向代理

除了正向代理外,反向代理也可以解决跨域限制。反向代理解决跨域的思路是有两种

  1. 对反向代理服务器使用CORS,这种方法本质上仍然是CORS方法,但是它的意义在于设置好反向代理的跨域资源共享(CORS)后,所有目标服务器不用再做任何处理,只需要设置好反向代理即可。
  2. 利用反向代理将后端接口代理到与网站的同源路径下。

JSONP

JSONP全称是json with Padding,这是一种比较老的解决方案。

jsonp的思路是

  1. 首先定义好回调函数,后端的数据作为回调函数的参数。
  2. 动态创建script标签请求后端接口
  3. 后端返回回调执行语句,并将数据作为参数传过来。
// 服务器 nodejs
const http = require("http");
const server = http
  .createServer((req, res) => {
		const data = 123 // 取数据
    res.end(`cb(${data})`);  // 数据作为参数
  })
  .listen("3333");
// 浏览器
<script>
		// 定义好回调函数,后端数据会当做参数传入
    function cb(data) {
      console.log(`data是${data}`);
    }
    const scr = document.createElement("script");
    scr.setAttribute("src", "<http://localhost:3333>");
    document.documentElement.appendChild(scr);
</script>

jsonp解决跨域的本质是利用了script 等html标签所发送的http请求不受到同源策略的限制这一特性,script请求的内容会作为JavaScript代码直接执行,因此只需要和后端约定好回调函数名、要传回的数据等就可以使用jsonp完成跨域。

jsonp的优点是兼容性好,缺点也很明显:使用起来繁琐;只支持get方法;不安全,容易被跨站脚本攻击等。