一.跨域的实质
服务器的跨域问题,在于如果进行前后端分离的网站开发时,前端与后端的域名、协议、端口不一致,导致浏览器进行的服务请求拦截。但其实虽然说浏览器将请求拦截下来,但是请求依然是请求成功,只不过服务器没有将数据在网页中进行渲染。
跨域,其实也叫同源策略。
同源策略(Same origin policy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。可以说Web是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。
同源策略,它是由Netscape提出的一个著名的安全策略。现在所有支持JavaScript
的浏览器都会使用这个策略。所谓同源是指,域名,协议,端口相同。
如果说一旦浏览器发现请求的服务器与发起请求的服务器不是同源, 则会主动进行拦截,为了保证服务器的数据安全。
如果说现有两个Django
项目,项目2向项目1的服务器请求数据,那么一旦发起了这次请求,请求是成功的,数据也会得到,但是浏览器在渲染的时候会进行拦截报错:
已拦截跨源请求:同源策略禁止读取位于 http://127.0.0.1:7766/SendAjax/ 的远程资源。(原因:CORS 头缺少 'Access-Control-Allow-Origin')。
这是火狐浏览器中的错误提示,谷歌浏览器错误提示相同,只不过是英文的
针对跨域请求问题,如果使用Django
开发网站,根据请求的类型不同,有不同的处理方法。
二.CORS
CORS
需要浏览器和服务器同时支持。目前,所有浏览器都支持该功能,IE浏览器不能低于IE10。
整个CORS
通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS
通信与同源的AJAX
通信没有差别,代码完全一样。浏览器一旦发现AJAX
请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。
因此,实现CORS
通信的关键是服务器。只要服务器实现了CORS
接口,就可以跨源通信。
CORS
的两种请求
浏览器将CORS
请求分成两类:简单请求(simple request)和非简单请求(not-so-simple request)。
只要同时满足以下两大条件,就属于简单请求。
而简单请求需要同时满足以下两种条件:
(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
而只要不满足上述的两种条件,那就是复杂请求。
CORS
针对简单请求和复杂请求,进行处理请求跨域的方式是不一样的
针对简单请求处理请求跨域
简单请求进行跨域请求,如果不进行跨域的处理,那么浏览器的报错信息是:
Access to XMLHttpRequest at 'http://127.0.0.1:8008/books/' from origin 'http://127.0.0.1:8000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
其实就是服务器进行响应的时候,缺少一个响应头:Access-Control-Allow-Origin
,只需要在 响应给浏览器时,将这个响应头加上即可:
from django.http import JsonResponse
def books(request):
print("s2 books")
obj = JsonResponse(["python", 'linux', "go"], safe=False)
obj["Access-Control-Allow-Origin"] = "*"
return obj
添加响应头时,右侧的*
,代表任何请求,不管是从哪个域名向服务器发起的请求,都予以通过。当然,也可以专门设置某一个域名的跨域请求通过:
obj["Access-Control-Allow-Origin"] = "192.168.0.1:8000"
这表明只有从IP是192.168.0.1,且端口是8000的服务器发来的请求,能够成功访问。
如果是想要在一个Django
项目中的每个视图函数,都进行跨域的处理,那我们可以直接添加一个中间件来专门处理跨域请求。
注意:中间件是要处理服务器给出响应,所以要重写process_response
方法:
class MyMiddle(MiddlewareMixin):
def process_response(self, request, response):
response["Access-Control-Allow-Origin"] = "*"
return response
针对复杂请求处理请求跨域
复杂请求,例如浏览器向服务器发送DELETE
,PUT
等请求时,那么浏览器会向服务器发送一个预检请求,来询问服务器的跨源策略,如果配置符合,则继续发送请求,否则就会抛出错误。
而浏览器发送的预检请求,一般都是OPTIONS
类型的请求。
此时,我们需要在服务器的响应中,再设置一个头信息:Access-Control-Allow-Origin
,即可完成预检请求,实现跨域。
from django.http import JsonResponse
def books(request):
print("s2 books")
obj = JsonResponse(["python", 'linux', "go"], safe=False)
obj["Access-Control-Allow-Headers"]="Content-Type,b"
obj["Access-Control-Allow-Origin"] = "*"
return obj
同样的,我们 可以用中间件来实现简单请求和复杂请求的跨域请求:
class MyMiddle(MiddlewareMixin):
def process_response(self, request, response):
response["Access-Control-Allow-Origin"] = "*"
response["Access-Control-Allow-Headers"]="Content-Type,b"
return response