说起跨域,开发过前后端的程序员都不陌生,但是大多数人还是知其然而不知其所以然。 在和别人讲对跨域的理解的时候,发现自己其实并不是特别熟悉。只是会用,为什么这么用,我说不清楚。所以还是再系统的学一下。
情景:前端项目通过网络请求从后端项目中获取数据,后端显示访问正常,没有任何报错,而在前端却收不到数据,还会出现报错。遇见这种情况,很大可能是出现了跨域问题。那么跨域问题发生在前端与后端的哪个阶段?
1、什么是跨域?
跨域是前后端分离的项目绕不开的话题。 因为前端和后端是不同源的。 说到不同源。又需要讲一下同源策略
同源策略,是指浏览器的同源策略, 它是浏览器最核心,也是最基本的安全功能。
没有浏览器的同源策略,就不会有跨域的问题。
登录baidu.com (为了简便,利用其它网站的前端项目)按F12,打开console, 输入如下代码:
var xhr = new XMLHttpRequest();
xhr.open('GET', '跨域请求网址');
xhr.send(null);
xhr.onload = function(e) {
var xhr = e.target;
console.log(xhr.responseText);
}
出现类似这样的报错:No 'Access-Control-Allow-Origin' header is present on the requested resource.
但是后端是能够正常接收到请求的, 如下图:
分析: 前后端分离的项目出现了报错,问题既不是出在前端,也不是出在后端。那么还剩下一种情况:浏览器
我们在浏览器输入网址时,浏览器会先向前端项目发起数据请求,然后前端项目向浏览器返回数据,而这些数据中,包含了让浏览器向后端继续获取数据的命令,这时,浏览器会执行这些命令,向后端项目获取数据请求,后端项目也会相应地将数据返回给浏览器,但是浏览器会选择拒绝接受此数据,并报出产生跨域问题的错误信息。这个情况,就是浏览器同源策略起了作用。
二、跨域问题的解决思路
解决跨域问题,方案有很多,有的基于前端的配置, 有的是基于后端的配置。 还有的基于中间件的反向代理的方式。
1. 通过jsonp跨域 (基于前端配置)
原理: <script>标签不受浏览器同源策略限制,比如在线引用jQuery, 其实也是一种跨域的实现:
<script src="http://code.jquery.com/jquery-latest.js"></script>
注意:jsonp 有很大的局限性,就是只能实现get请求
2. document.domain + iframe 跨域 (基于后端配置)
原理:不同域指向的两个页面,都通过JS强制设置document.domain 为基础主域, 就可以实现同域。
(1)父窗口 www.domain.com/a.html:
<iframe id="iframe" src="http://child.domain.com/b.html"></iframe>
<script>
(2) 子窗口 child.domain.com/b.html:
<script>
注意:此方案仅限于主域相同,子域不同的跨域应用
3. CORS(跨域资源共享)(基于后端配置)
通过在服务端设置 Access-Control-Allow-Origin 即可解决跨域问题, 前端无需设置。
此方案是目前主流的跨域解决方案
CORS 是一个W3C标准
CORS的原理是只需要向响应头header 中注入Access-Control-Allow-Origin ,浏览器检测到header中有这个Access-Control-Allow-Origin,就可以进行跨域操作了。
CORS 允许浏览器向跨源服务器发送XMLHttpRequest 请求,从而克服Ajax 只能同源使用的限制
4. Nginx 代理跨域
(1) Nginx 配置解决iconfont 跨域。浏览器跨域访问JS,CSS和Img等常规静态资源被同源策略许可,但iconfont字体文件(eot/otf/ttf/woff/svg)例外,此时可在Nginx的静态资源服务器中加入以下配置:
location / {
add_header Access-Control-Allow-Origin *;
}
(2)Nginx 反向代理
原理: 通过Nginx 配置一个代理服务器(域名和domain1相同,端口不同) 做跳板机, 反向代理访问domain2接口。服务器调用HTTP接口只是使用HTTP协议,不会执行JS脚本,因为不需要同源策略,也就不存在跨域问题。
小结: 上面只介绍了4种常见的跨域解决方案。其实还存在着很多种解决方案。这些解决方案都可以总结出一个规律,那就是所有的解决方案都是在绕开一件事,那就是尽量绕开JS脚本中于网络请求有关的命令,浏览器对于JS脚本做网络请求的限制非常大,在浏览器中运行的JS脚本也因此不可以作为网络爬虫来运行,因为浏览器中运行的JS脚本在发送网络请求时,是不允许自定义协议头的。
三、解决跨域问题
下面是基于后端项目的解决方案:
(1) pip install django-cors-headers
(2) 在settings 中注册模块, 增加中间件的配置代码, 能够跨域的白名单:
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'app01.apps.App01Config',
'rest_framework',
'corsheaders'
]
MIDDLEWARE = [
'corsheaders.middleware.CorsMiddleware', # 放到中间件顶部
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
CORS_ORIGIN_ALLOW_ALL = False
CORS_ORIGIN_WHITELIST = (
'http://192.168.9.2:8080',
'https://ai.domain.com',
"https://django.domain.com",
)
CORS_ALLOW_CREDENTIALS = True
CORS_ALLOW_HEADERS = (
'lpt',
'XMLHttpRequest',
'content-type',
'accept',
'accept-encoding',
'authorization',
'dnt',
'origin',
'user-agent',
'x-csrftoken',
'x-requested-with',
)