浏览器的同源策略及跨域解决方案
主要内容:
- 1. 浏览器的同源策略
- 2. jsonp解决跨域
- 3. cors解决跨域
- 4.使用 django-cors-headers
1. 浏览器的同源策略
1.1 同源策略的概述 同源指的是: 两个页面具有相同的协议,端口,和域名 (同一个服务器上的资源)
- 同源策略是浏览器的一个安全功能,不同源的客户端脚本在没有明确授权的情况下,不能读写对方资源。所以xyz.com下的js脚本采用ajax读取abc.com里面的文件数据是会被拒绝的。
- 同源策略限制了从同一个源加载的文档或脚本如何与来自另一个源的资源进行交互。这是一个用于隔离潜在恶意文件的重要安全机制
1.2 同源策略的机制: 限制了浏览器往不同的源发请求,阻止读取响应数据
向服务器发起请求,服务器正常响应,但是浏览器会对响应的进行判断,如果客户端与服务端不是一个源就会拦截响应
跨域访问: 浏览器进制读取响应内容
应用:前后端分离的项目:虽然是在同一个服务器上,但是端口号不同,同样存在同源策略的问题
1.3 不受同源策略限制的情况
- 1. 页面中的链接,重定向以及表单提交是不会受到同源策略限制的。(如页面中的a标签,页面上的form表单,以及重定向)
- 2. 跨域资源的引入是可以的。但是js不能读写加载的内容。如嵌入到页面中的<script src="..."></script>,<img>,<link>,<iframe>等 (如cdn方式导入的jquery等虽然跨域但是能加载到数据)
2. jsonp 解决跨域问题
2.1 jsonp原理解析
示例:
- 通过一个html页面,页面将请求地址放入scrip标签中
<button id="b1">点我发请求</button> <script src="http://127.0.0.1:8000/index/"></script>
- 后台django项目views.py中
def index(request): return HttpResponse("func")
当html页面向django的index页面发起请求,浏览器打印如下
看来后端返回的响应已经被拿到了,只不过把func当成了一个变量来使用,但是该页面上却没有定义一个名为func的变量。所以出错了。
修改:
- 后端返回的一个待执行的函数
def index(request): return HttpResponse("func()")
- html代码中进行调用func函数
<button id="b1">点我发请求</button> <script> function func(data) { console.log("我是自定义函数"); } </script> <script src="http://127.0.0.1:8000/index/"></script>
浏览器打印
基于此:
- 后端index函数 ,HttpResponse
def index(request): data = {"name":"alex","age":9000} return HttpResponse('func({})'.format(json.dumps(data)))
- html页面
<button id="b1">点我发请求</button> <script> function func(data) { console.log(data); } </script> <script src="http://127.0.0.1:8000/index/"></script>
浏览器获取数据
小结: 利用 浏览器加载静态资源的时候不限制跨域
-
名字是个函数名,前端定义好的函数名
-
函数后面加括号,并且括号中把要传递的数据放进
2.2 jquery中的jsonp
- django 后台
def index(request): data = {"name":"alex","age":9000} func_name = request.GET.get('callback') return HttpResponse('{}({})'.format(func_name,json.dumps(data)))
- html页面
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <button id="b1">点我发请求</button> <script src="jquery-3.3.1.min.js"></script> <script> // 点击b1按钮 发送 jsonp请求 $('#b1').click(function () { // 使用jQuery提供的getJSON方法来发送JSONP请求获取数据 $.getJSON('http://127.0.0.1:8000/index/?callback=?', function (data) { console.log(data); }) }) </script> </body> </html
浏览器 :
jquery中随机产生一个函数名,随着请求发送到后端,后端将函数名,以及数据返回,jsonp请求结束执行回调函数
缺点:
- 前后端都要支持
- 只能发GET请求
3. CORS解决跨域
3.1 CORS概述
- CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing)。它允许浏览器向跨源服务器发出XMLHttpRequest请求,从而解决AJAX只能同源使用的限制
- CORS需要浏览器和服务器同时支持。目前基本上主流的浏览器都支持CORS。所以只要后端服务支持CORS,就能够实现跨域。
3.1 简单请求和非简单请求
浏览器将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
(1)简单请求的处理
在跨域场景下,当浏览器发送简单请求时,浏览器会自动在请求头中添加表明请求来源的 Origin 字段。
我们的后端程序只需要在返回的响应头中加上 Access-Control-Allow-Origin 字段,并且把该字段的值设置为 跨域请求的来源地址或简单的设置为 * 就可以了
from django.utils.deprecation import MiddlewareMixin class CorsMiddleware(MiddlewareMixin): def process_response(self, request, response): # 给响应头加上 Access-Control-Allow-Origin 字段 并简单的设置为 * response['Access-Control-Allow-Origin'] = '*' return response
(2)非简单请求的处理
- 我们开发中常用到的那些请求方法是PUT或DELETE,或者Content-Type字段的类型是application/json的都是非简单请求。
- 对于非简单请求,浏览器通常都会在请求之前发送一次 OPTIONS 预检 请求。该请求会像后端服务询问是否允许从当前源发送请求并且询问允许的 请求方法 和 请求头字段
解决办法也很简单,我们可以在后端简单的给响应对象添加上 常用请求方法(PUT、DELETE)的支持就可以了。
from django.utils.deprecation import MiddlewareMixin class CorsMiddleware(MiddlewareMixin): def process_response(self, request, response): # 给响应头加上 Access-Control-Allow-Origin 字段 并简单的设置为 * response['Access-Control-Allow-Origin'] = '*' if request.method == 'OPTIONS': # 允许发送 PUT 请求 response['Access-Control-Allow-Methods'] = 'PUT, DELETE' # 允许在请求头中携带 Content-type字段,从而支持发送json数据 response['Access-Control-Allow-Headers'] = 'Content-type' return response
4. 使用django-cors-headers
- 安装
pip install django-cors-headers
- 注册APP
INSTALLED_APPS = [ ... 'app01.apps.App01Config', 'corsheaders', # 将 corsheaders 这个APP注册 ]
- 添加中间件 必须放在最前面,因为要先解决跨域的问题。只有允许跨域请求,后续的中间件才会正常执行
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 = True
- 设置允许访问的白名单
CORS_ORIGIN_ALLOW_ALL = False CORS_ORIGIN_WHITELIST = ( # '<YOUR_DOMAIN>[:PORT]', '127.0.0.1:8080' )
settings配置相关

INSTALLED_APPS = [ ... 'corsheaders', ... ] MIDDLEWARE_CLASSES = ( ... 'corsheaders.middleware.CorsMiddleware', 'django.middleware.common.CommonMiddleware', # 注意顺序 ... ) #跨域增加忽略 CORS_ALLOW_CREDENTIALS = True CORS_ORIGIN_ALLOW_ALL = True CORS_ORIGIN_WHITELIST = ( '*' ) 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', )
博客参考自:https://www.cnblogs.com/liwenzhou/p/9513648.html