跨域问题
同源策略
1、同源策略,是浏览器为了保护用户信息安全的一种安全机制
2、所谓的同源就是指代通信的两个地址(例如服务端接口地址与浏览器客户端页面地址)之间比较,是否协议、域名(IP)和端口相同
3、不同源的客户端脚本[javascript]
在没有得到服务端的明确授权的情况下,浏览器会拒绝显示服务端信息提供给前端ajax/axios
CORS(跨域资源共享)简介
1、CORS是一个W3C标准,全称是"跨域资源共享",它允许浏览器向跨源的后端服务器发出ajax请求,从而克服了AJAX只能同源使用的限制。
实现CORS主要依靠后端服务器中响应数据中设置响应头信息返回的
2、CORS需要浏览器和服务器同时支持。目前,所有浏览器都支持该功能,IE浏览器不能低于IE10。
3、整个CORS通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS通信与同源的AJAX通信没有差别,代码完全一样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。
4、因此,实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信。
注:容易混淆的点:
- CORS:跨域资源共享
- CSRF:跨站请求伪造
- XSS:跨站脚本攻击
CORS基本流程
浏览器将CORS请求分成两类:简单请求(simple request)和非简单请求(not-so-simple request)。
浏览器发出CORS简单请求,只需要在头信息之中增加一个Origin字段。
浏览器发出CORS非简单请求,会在正式通信之前,增加一次HTTP查询请求,称为”预检”请求(preflight)。
浏览器先询问服务器,当前网页所在的域名是否在服务器的许可名单之中,以及可以使用哪些HTTP动词和头信息字段。
只有得到肯定答复,浏览器才会发出正式的XMLHttpRequest请求,否则就报错。
CORS两种请求详情
1、简单请求和非简单请求
只要同时满足以下两大条件,就属于简单请求。
(1) 请求方法是以下三种方法之一: HEAD GET POST (2)HTTP的头信息不超出以下几种字段: Accept Accept-Language Content-Language Last-Event-ID Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain
凡是不同时满足上面两个条件,就属于非简单请求。
2、浏览器对这两种请求的处理,是不一样的。
简单请求和非简单请求的区别
简单请求:一次请求,直接发真正的请求,如果允许,数据拿回来,如果不允许,浏览器拦截
非简单请求:两次请求,在发送数据之前会先发一次请求用于做“预检”,只有“预检”通过后才再发送一次请求用于数据传输,非简单请求发两次,第一次是OPTIONS请求,如果允许跨域,再发真正的请求
预检:检查如果通过则允许传输数据,检查不通过则不再发送真正想要发送的消息
如何预检:
如果复杂请求是PUT等请求,则服务端需要设置允许某请求,否则“预检”不通过
Access-Control-Request-Method
如果复杂请求设置了请求头,则服务端需要设置允许某请求头,否则“预检”不通过
Access-Control-Request-Headers
解决跨域问题(服务端)
简单请求
1、原Django项目:apps/user/views.py
from django.http import JsonResponse def test(request): obj = JsonResponse({'name':'279工程','price':6000000}) # 值针对简单请求 obj['Access-Control-Allow-Origin'] = '*' # 允许所有IP访问 return obj
2、原Django项目:apps/user/urls.py
from django.urls import path from user import views urlpatterns = [ path('test/', views.test), ]
3、注释掉CSRF
4、在创建1个Django项目(用另外的端口)
emplates中创建index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js"></script> </head> <body> <button id="btn">点我</button> </body> <script> $('#btn').click(function () { $.ajax({ url: 'http://127.0.0.1:8000/user/test/', method: 'get', success: function (data) { console.log(data) } }) }) </script> </html>
viwes.py
from django.shortcuts import render def index(request): return render(request, 'index.html')
urls.py
from django.urls import path from app01 import views urlpatterns = [ path('test/', views.index), ]
非简单请求
原Django项目apps/user/views.py
from django.http import JsonResponse def test(request): obj = JsonResponse({'name': 'Darker', 'age': '18'}) if request.method == 'OPTIONS': obj['Access-Control-Allow-Headers'] = 'Content-Type,authorization' # 或者填写 * obj['Access-Control-Allow-Origin'] = 'http://127.0.0.1:8002' # 8002端口是当前项目的 return obj
新Django项目templates/index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js"></script> </head> <body> <button id="btn">点我</button> </body> <script> $('#btn').click(function () { let obj = {name: 'Darker'} $.ajax({ url: 'http://127.0.0.1:8000/user/test/', method: 'post', contentType: 'application/json', headers: {authorization: 'Darker'}, data: JSON.stringify(obj), success: function (data) { console.log(data) } }) }) </script> </html>
中间件处理
在原Django项目的根路径创建mymiddle.py
- 自定义中间件
from django.utils.deprecation import MiddlewareMixin class CoreMiddle(MiddlewareMixin): def process_response(self, request, response): if request.method == 'OPTIONS': response['Access-Control-Allow-Headers'] = 'Content-Type, authorization' # 如果是 * 就代表全部IP都可以访问 response['Access-Control-Allow-Origin'] = '*' return response
在原Django项目的dev.py
的中间件中添加自定义中间件
MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'mymiddle.CoreMiddle', # 这一句是新加的 'django.middleware.common.CommonMiddleware', # 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', ]
原Django项目apps/user/views
中替换成如下代码
from django.http import JsonResponse def test(request): return JsonResponse({'name': 'Darker', 'age': '18'})
解决跨域问题(第三方)
下载
pip install django-cors-headers
app注册
INSTALLED_APPS = [ ... 'corsheaders', ... ]
中间件注册
MIDDLEWARE = [ # Or MIDDLEWARE_CLASSES on Django < 1.10 ... 'corsheaders.middleware.CorsMiddleware', ... ]
配置文件配置
# 允许简单请求,所有地址 相当于CORS_ORIGIN_ALLOW_ALL="*" CORS_ALLOW_ALL_ORIGINS = True # 运行的请求 CORS_ALLOW_METHODS = ( 'DELETE', 'GET', 'OPTIONS', 'POST', 'PUT', ) # 允许的请求头 CORS_ALLOW_HEADERS = ( 'accept-encoding', 'authorization', # jwt 'content-type', # json 'origin', 'user-agent', 'Pragma', )