CORS与Django

前言

   在前后端分离项目中,如何解决跨域请求是一个必须要面对的问题。因为前端和后端的数据交互会被浏览器的同源策略所挟持,在很早之前我在博客园发了一篇文章,大概就说了一下如何简单粗暴的解决跨域。

   其实那种解决办法是不适用于内部项目的,而是对一些公共的开发接口做访问进行数据获取,下面是文章链接:

   jQuery JSONP解决跨域请求

CORS

   CORS就是跨域资源共享的意思,它需要浏览器和服务器同时支持。目前,所有浏览器都支持该功能,IE浏览器不能低于IE10。

   整个CORS通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS通信与同源的AJAX通信没有差别,代码完全一样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。

   因此,实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信。

基本流程

   浏览器将CORS请求分成两类:简单请求(simple request)和非简单请求(not-so-simple request)。 浏览器发出CORS简单请求,只需要在头信息之中增加一个Origin字段。 浏览器发出CORS非简单请求,会在正式通信之前,增加一次HTTP查询请求,称为”预检”请求(preflight)。浏览器先询问服务器,当前网页所在的域名是否在服务器的许可名单之中,以及可以使用哪些HTTP动词和头信息字段。只有得到肯定答复,浏览器才会发出正式的XMLHttpRequest请求,否则就报错。

请求区别

   只要同时满足以下两大条件,就属于简单请求,否则就是非简单请求。

  1. 请求方法是以下三种方法之一:

HEAD

GET

POST

  1. HTTP的头信息不超出以下几种字段:

Accept

Accept-Language

Content-Language

Last-Event-ID

Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain

处理情况

   浏览器对于上面两种请求处理,会有不一样的方式:

   简单请求

   浏览器只会发送一次请求,然后后端将会返回数据,此时后端如果没有做跨域资源请求的允许,数据将会被浏览器同源策略所拦截。

   非简单请求

   浏览器会发送两次请求,第一次请求方式为OPTIONS,大概就是做一个预检查,检查后端是否允许第二次请求的发送。

   如果允许,才会发送第二次请求,后端会将第二次请求的数据返回,如果后端没有做跨域资源请求的允许,数据将会被浏览器同源策略所拦截。

   如果不允许,浏览器就不会发送第二次请求。

后端解决

   对于简单请求来说,我们只需要允许跨域资源共享即可。

   所以只需要在后端设置一个请求头:

Access-Control-Allow-Origin = "域名或者*"

   对于非简单请求来说,我们需要让预检进行通过,此外还需要允许跨域资源共享,所以需要设置最少设置两个或多个请求头:

  • “预检”请求时,允许请求方式则需服务器设置响应头:Access-Control-Request-Method
  • “预检”请求时,允许请求头则需服务器设置响应头:Access-Control-Request-Headers
# 这两个是让预检通过
Access-Control-Allow-Headers = "Content-Type"
Access-Control-Allow-Headers = "authorization"  #JWT认证

# 下面这个是允许资源共享
Access-Control-Allow-Origin = "域名或者*"

基本总结

   一言以蔽之,当前端跨域向后端发送、请求数据时,有可能发简单请求,也有可能发非简单请求。

   简单请求只会发一次,而非简单请求会发两次请求。

   如果是简单请求,数据就会直接返回,如果是非简单请求,则需要后端做出一些相应的设置才会允许第二次真正请求的发送。

   对于非简单请求的预检来说,它其实是规定了前端能够去使用哪些请求方式,如GET/POST,同时还允许请求头中有什么,如Content-Type以及JWT随机字符串。

   我举个例子,如果前端要跨域通过POST请求发送一个JSON数据那么它的流程如下:

    1. 发送OPTIONS请求进行预检,检测是否允许带有Content-Type请求头(因为JSON格式必须由该请求头声明)
    2. 预检通过后,发送POST请求,Content-TypeJSON,带上数据发送过去。

Django解决

中间件

   对于Django来说,我们可以将处理设置在中间件中。不管你是使用drf还是原生Django,都可以这样设置,因为此时的request对象并未被包装。

   下面是同时支持简单请求和非简单请求的示例:

from django.utils.deprecation import MiddlewareMixin

class CorsMiddle(MiddlewareMixin):
    def process_response(self, request, response):
        response['Access-Control-Allow-Origin'] = '*'  # 允许跨域,解决同源策略。简单和非简单请求都需要设置这个,可以加地址,只允许这个地址的请求跨域,如HTTP://127.0.0.1:2333
        if request.method == "OPTIONS":
            # 可以加*,代表允许所有。 这里是预检的OPTIONS请求,它允许Content-Type请求头和authorization请求头。
            response["Access-Control-Allow-Headers"] = "Origin,Content-Type,Cookie,Accept,Token,authorization" 

        return response
        
# 记得在setting的中间件中配置

django-cors-headers

   这是一个模块,可以很好的解决同源策略问题,以及非简单请求的预检问题。

   下载安装即可:

pip install django-cors-headers

   然后需要进行注册:

INSTALLED_APPS = (
	'corsheaders',
)

   添加中间件(其实它的解决方案和上面写的思路完全一模一样):

MIDDLEWARE = [  # Or MIDDLEWARE_CLASSES on Django < 1.10
	...
	'corsheaders.middleware.CorsMiddleware',  # 写上面,因为Django中间件的返回顺序是自下而上。
	...
]

   在settings.py中进行配置:

CORS_ALLOW_CREDENTIALS = True
CORS_ORIGIN_ALLOW_ALL = True  # 允许异端请求,解决同源策略


CORS_ALLOW_METHODS = (  # 允许的请求方式
	'DELETE',
	'GET',
	'OPTIONS',
	'PATCH',
	'POST',
	'PUT',
	'VIEW',
)

CORS_ALLOW_HEADERS = (  # 允许的请求头
	'XMLHttpRequest',
	'X_FILENAME',
	'accept-encoding',
	'authorization',
	'content-type',
	'dnt',
	'origin',
	'user-agent',
	'x-csrftoken',
	'x-requested-with',
	'Pragma',
)
posted @ 2020-11-11 15:35  云崖先生  阅读(252)  评论(0编辑  收藏  举报