[Re]跨域和跨域问题的解决

in 默认 with 0 comment

[Re] 表示在服务器出问题之前就写过的文章,被重新上线或重新编辑的相同话题

跨域

浏览器的安全机制不允许跨域请求。但在前后端分离的研发流程中,后端和前端显然不是,也不会是同一个域。这种时候就产生了跨域

同源和跨域

以下情况会产生跨域问题:

请求页面被请求是否跨域原因
http://www.mitkimi.comhttp://www.mitkimi.com/api
http://www.mitkimi.comhttp://www.mitkimi.com:8080/api端口不一致
http://www.mitkimi.comhttp://api.mitkimi.com/域名不一致
http://www.mitkimi.comhttp://www.abc.com/域名不一致
http://www.mitkimi.comhttps://www.mitkimi.com/api协议不一致

所以“同源”实际上指的是协议、域名、端口要一致。任何一种不同都会产生“跨域”。

非同源限制

在非同源的情况下,浏览器出于安全和网站隔离等原因的考虑,会出现不少问题:
例如,cookie、localStorage 和 indexedDB 是无法非同源获取的。
同时,无法向非同源地址发送 AJAX 请求,也无法接触非同源网页的 DOM。
以上问题都是所谓的“跨域”问题。

需跨域的场景

单点登录

一个大型网站,不同功能可能需要不同的的子域,但是他们都在同一个主域下,需要共享登录状态(例如淘宝的登录域名和主要业务域名不在一个子域下)。
这种情况需要完成单点登录

跨域异步请求(研发阶段)

前后端分离的浏览器端异步请求会被浏览器机制挡在门外,这种情况需要处理请求问题。

解决

跨域问题是可以通过一些技术手段解决的。

单点登录

登录状态我们通常会存在只有 4kb 空间的 cookie 里,但非同源时无法共享登录状态。
由于浏览器是通过 document.domain 属性来检查两个页面是否同源的。这时候如果我们手动设置相同的属性,既可以解决无法读取非同源网页的 Cookie问题。

// 在入口文件
document.domain = 'mitkimi.com'

异步请求(AJAX API)

异步请求跨域是最常见的跨域问题。我们说的“跨域问题”一般情况不特指的情况下也指的是这种情况。解决跨域请求问题有很多种方法。

由于有些方法不能达到很好的通用效果,所以这里仅介绍通用的解决方法或对于其他方法一带而过

CORS

CORS 是跨域资源分享(Cross-Origin Resource Sharing)的缩写。它是 W3C 标准,属于跨源 AJAX 请求的根本解决方法。

  1. 普通跨域请求:只需服务器端设置Access-Control-Allow-Origin
  2. 带cookie跨域请求:前后端都需要进行设置

Access-Control-Allow-Origin 属性可以设置为 * 表示所有人都可以跨域请求,也可以设置为指定的域名进行请求。
当设置了 Access-Control-Allow-Origin 属性后有一个致命的问题,即设置为 * 时,所有人都可以调通,也就是说所有人都可以使用这个项目。安全性就没有办法保障。
即便是把 Access-Control-Allow-Origin 属性设置为指定的域名依旧无法保障安全,因为可以修改本地 hosts 覆盖掉 DNS 服务的域名解析,相当于绕过了域名检测。
但对于设计来就是用作公共数据接口的,用这种方法解决跨域问题可以省去前端研发人员的时间。

代理

由于在生产环境中可以在 nginx 上把服务端代理到 /api 上,所以我们在开发阶段也可以使用这种代理的思路,在开发环境下使用相关的技术代理到开发环境下的 /api 上即可达到目的。

vue

vue-cli 3.0 使用的是 http-proxy-middleware 这个模块,也可以在之前的版本使用
首先在项目根目录建立 vue.config.js 文件

module.exports = {
  devServer: {
    proxy: {
      '/api': {
        target: 'http://10.1.5.5:8111/mitkimi-web', //对应自己的接口
        changeOrigin: true,
        ws: true,
        pathRewrite: {
          '^/api': ''
        }
      }
    }
  }
}

这样项目的开发环境就生成了一个代理,调用接口时就可以直接用 /api 代替域名,例如调用 http://10.1.5.5:8111/mitkimi-web/passport/signin 时,即可使用 /api/passport/signin 调用。

我写的 bug 可能要遗臭万年了...