前言

在某个晚上搭建 apiserver 和 webfe 环境,启动前后端项目后在浏览器访问服务器的域名,出现了返回的状态码为 200,但是前端却拿不到后端数据,即跨域问题。寻求 mt 的帮助后解决了这个问题,收获不小,记录一下

前后端跨域问题

说到跨域问题,就得谈谈浏览器的 同源策略,跨域也是因为浏览器的这个机制引起的,至于为什么会存在这个机制,是为了后端数据的访问安全。

  1. 什么是源?

Web 内容的 “源” 需要根据访问它的 URL 的协议、域名和端口定义。只有当协议、域名和端口都匹配时,两个对象具有相同的 “源”

同源不同源其实用一句话就可以判断:URL 中 protocol、host 和 port 都相同即为同源,否则不同源

  1. URL 结构

URL 指 “统一资源定位符(Uniform Resource Locator),用于给定一个独特资源在 Web 上的地址

URL 一般由如下结构组成:

  • Protocol

image.png

  • Host

image.png

  • Port

image.png

  1. 为什么需要同源策略?

如果浏览器没有 同源策略,恶意网站可以随意读取其他网站的敏感信息(比如登录态、个人信息等),或者篡改其他网站的内容,这样的话网络安全就彻底没保障了。

举个 🌰,当你在 https://bank.com 登陆了网上银行(cookie 里有你的身份信息),同时又打开了一个 https://bad.com 的恶意网站,因为 bank.combad.com 域名不同,即不同源,此时同源策略会阻止 bad.com 的代码读取 bank.com 的 cookie,也不能调用 bank.com 的接口转账,这就避免了你的账户被盗。

  1. 到底什么是跨域问题?

当你的网页,比如 a.com 试图通过 JavaScript 访问另一个源 b.com 的资源时,浏览器的 同源策略 就会阻止这个请求,这就是 跨域问题

比如下面一些常见的场景:

  • 前端 localhost:8000 调用后端 API api.example.com
  • 网站 www.site.com 想加载 CDN 上的资源 cdn.site.com

前言中提到的问题就是 前端想要调用后端 API ,并且请求能被正确响应,却被浏览器的同源策略拦截导致无法拿到后端数据

当时的具体场景是,前端跑在 app.dev.com:6060 上,后端跑在 app.dev.com:8000 上,出现了 端口不同源,当前端请求后端时,后端返回的数据到达浏览器,此时浏览器发现请求的地址端口与后端端口不同,于是判断为 跨域请求,不让前端访问到后端返回的数据。

解决跨域?CORS?

既然前后端分离项目存在跨域问题,那么如何解决?我们总不能让前端项目拿不到后端数据吧,要不然咋渲染数据展示给用户。

这里就需要了解一种机制:CORS(Cross-Origin Resource Sharing),即 跨域资源共享

CORS 是一种基于 HTTP 头的机制,它允许浏览器向跨源服务器发起 XMLHttpRequst 或 Fetch 请求,从而绕过浏览器的同源策略限制。

  1. CORS 的原理是啥?

同源策略限制了一个源的网页脚本不能与不同源的服务器进行交互,而 CORS 通过 在服务端 返回特定的 HTTP 响应头,来告知浏览器哪些源被允许访问该服务器上的资源,从而实现跨域访问。

  1. 前言提到的 webfe 如何拿到 apiserver 返回的信息?

webfe 是使用 vue 编写的前端项目,apiserver 是基于 django 框架编写的后端项目,由于跑在同一服务器的不同端口上,存在跨域访问,需要解决这个跨域问题才能让前端正常显示。

具体的做法,需要在虚拟环境安装 django-cors-headers,并且在 settings.py 中的 INSTALLED_APPS 列表添加 corsheaders

INSTALLED_APPS = [
   ...
    'corsheaders',
   ...
]

然后在 settings.py 中将 corsheaders.middleware.CorsMiddleware 添加到 MIDDLEWARE 列表中,并且确保它在 django.middleware.common.CommonMiddleware 之前,因为它需要先处理跨域相关逻辑

最后在 settings.py 文件里进行相关配置就可以啦,比如下面这样子:

CORS_ALLOWED_ORIGINS = [
    "http://app.dev.com:6060",
]
# 如果需要允许所有源访问(不推荐在生产环境使用)
# CORS_ALLOW_ALL_ORIGINS = True 
# 允许携带凭证(如 cookie),如果前端需要携带凭证,需开启此项并确保前端设置正确
# CORS_ALLOW_CREDENTIALS = True 
# 允许的请求方法,可根据实际情况增减
CORS_ALLOW_METHODS = [
    "DELETE",
    "GET",
    "OPTIONS",
    "PATCH",
    "POST",
    "PUT",
]
# 允许的请求头,可根据实际情况增减
CORS_ALLOW_HEADERS = [
    "accept",
    "accept-encoding",
    "authorization",
    "content-type",
    "dnt",
    "origin",
    "user-agent",
    "x-csrftoken",
    "x-requested-with",
]

参考