前言
在某个晚上搭建 apiserver 和 webfe 环境,启动前后端项目后在浏览器访问服务器的域名,出现了返回的状态码为 200,但是前端却拿不到后端数据,即跨域问题。寻求 mt 的帮助后解决了这个问题,收获不小,记录一下
前后端跨域问题
说到跨域问题,就得谈谈浏览器的 同源策略,跨域也是因为浏览器的这个机制引起的,至于为什么会存在这个机制,是为了后端数据的访问安全。
- 什么是源?
Web 内容的 “源” 需要根据访问它的 URL 的协议、域名和端口定义。只有当协议、域名和端口都匹配时,两个对象具有相同的 “源”
同源不同源其实用一句话就可以判断:URL 中 protocol、host 和 port 都相同即为同源,否则不同源
- URL 结构
URL 指 “统一资源定位符(Uniform Resource Locator),用于给定一个独特资源在 Web 上的地址
URL 一般由如下结构组成:
- Protocol
- Host
- Port
- 为什么需要同源策略?
如果浏览器没有 同源策略,恶意网站可以随意读取其他网站的敏感信息(比如登录态、个人信息等),或者篡改其他网站的内容,这样的话网络安全就彻底没保障了。
举个 🌰,当你在 https://bank.com
登陆了网上银行(cookie 里有你的身份信息),同时又打开了一个 https://bad.com
的恶意网站,因为 bank.com
和 bad.com
域名不同,即不同源,此时同源策略会阻止 bad.com
的代码读取 bank.com
的 cookie,也不能调用 bank.com
的接口转账,这就避免了你的账户被盗。
- 到底什么是跨域问题?
当你的网页,比如 a.com
试图通过 JavaScript 访问另一个源 b.com
的资源时,浏览器的 同源策略 就会阻止这个请求,这就是 跨域问题。
比如下面一些常见的场景:
- 前端
localhost:8000
调用后端 APIapi.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 请求,从而绕过浏览器的同源策略限制。
- CORS 的原理是啥?
同源策略限制了一个源的网页脚本不能与不同源的服务器进行交互,而 CORS 通过 在服务端 返回特定的 HTTP 响应头,来告知浏览器哪些源被允许访问该服务器上的资源,从而实现跨域访问。
- 前言提到的 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",
]