跨域 Cross origin2021-11-28

什么是跨域

假设一种情景

我对某网站要搞点小心思,因为对方服务器进不了,所以我把小心思放在我的服务器,封装成一个接口暴露出去,然后等我使用他的网站时,再请求我服务器的接口,搞烂他!ttk !

我真聪明!

。。。。。。

origin cors?好吧这个方法好像行不通,那我换个方法,吧小心思包装成一个软件,在互联网传播,等他服务器下载之后运行,我再植入到他的网站首页的script标签运行,偷偷冲他的网站!!嘿嘿嘿我真聪明!

W8JCre

<!-- index.html -->
<body>
  Orin的网站
</body>
<script>
  // 我们通过某种手段想让对方网站运行我们的端口的脚本
  fetch("http://localhost:8081").then(res=>{
    console.log("在这里做小动作");
  })
</scrip>

5t0d92

????

傻逼浏览器,阻止我积累战绩!ttk!!!

以上这些都是跨域导致的错误,跨域是浏览器为了请求安全而引入的基于同源策略的安全特性,需注意的是,这个跨域报错是浏览器的行为,和服务端没有关系,请求是正常返回了,只是卡在了浏览器这边

MDN是这样说明的:

  • 如果两个 URL 的 protocolport (en-US) (如果有指定的话)和 host 都相同的话,则这两个 URL 是同源。这个方案也被称为「协议/主机/端口元组」,或者直接是 「元组」。(元组是指一组项目构成的整体,双重/三重/四重/五重/等的通用形式)

    7bsguZ

我们上面的情况有host或者port不同,其他不变的情况,都造成了跨域问题,也叫同源策略

解决方案

目前较为流行的有三种

  1. cors
  2. jsonp
  3. 反向代理

1. CORS通信(Cross-Origin Resource Sharing)

cors是http的一部分,它允许服务端来指定哪些主机可以从这个服务端加载资源。采用cors的话只需要服务端修改一下请求头的Access-Control-Allow-Origin字段,该方案涉及到服务端,前端不需要修改,例如上面的例子我们可以改成:

除了上面这个请求头,添加的时候还有以下选择:

字段功能
Access-Control-Allow-Origin表示允许的来源
Access-Control-Allow-methods表示允许的请求方法
Access-Control-Allow-Headers表示允许的请求头
Access-Control-Allow-Credentials表示允许携带认证信息
app1.get("/", (req, resp) => {
  resp.header("Access-Control-Allow-Origin",'*')
 // 或者直接添加header字段「Access-Control-Allow-*」也可以
  resp.send("嘿嘿嘿,成功访问后我要搞烂你!!!")
});
<body>
  Orin的网站
</body>
<script>
 //前端再去访问该api就不会发生跨域问题了
  fetch("http://localhost:8081").then((res) => {
    res.text().then((data) => {
      alert(data);
    });
  });
</script>

HLbD7h

2. jsonp

JSONP 的原理是利用了浏览器加载 JavaScript 资源文件时不受同源策略的限制而实现的。把我们的请求变成一个资源请求,然后吧」小心思「弄成函数,当作资源请求的参数传入,这样吧函数在服务端处理后又返回去浏览器,就可以了!

<body>
  Orin的网站
</body>
<script>
  let trick = (data) => alert(date);
</script>
<script src="http://localhost:8081?callback=trick"></script>
app1.get("/", (req, resp) => {
  let trickFunction = req.query.callback;
  resp.send(trickFunction+"('嘿嘿嘿,成功访问后我要搞烂你!!!')");
});

RoEGTx

也是成功访问到api了

3. 反向代理

我在这里讲过了,就不多说了

更新于2022年3月前端解决跨域

之前有类似的问题,我都一直让后端解决,但最近遇到了调用第三方api,可人家我又不认识,不可能发个邮箱过去让人家给我弄吧?遂只能自己解决了。其思路就是做一个地址转发糊弄过去,如果使用webpack,到vue.config.js下的proxy字段,我用的是vite,就直接在对应的配置文件修改就行了,如下:

export default defineConfig({
  plugins: [vue()],
  server: {
    proxy: {
      // 前缀
      '/api': {
        // 转发的baseURL
        target: 'http://localhost:8080',
        // changeOrigin是否改变请求源的地址为目标地址
        // 我们这里只是解决了浏览器的跨域问题
        // 有一些服务器也会判断请求源是否和来自己,故可以更好的解决跨域问题
        changeOrigin: true,
        // 路径重写
        rewrite: (path) => path.replace(/^\/api/, '/')
      },
      '可以有多个':{...}
    }
  }
})

前缀自己改着来,需要注意的是用了proxy,就不需要配置Axios的baseURL了,自己封装请求的时候添加上去吧