同源策略限制了从同一个源架子啊的文档或脚本如何与来自另外一个源的资源进行交互。这是一个用于隔离潜在恶意文件的重要安全机制。css
同源策略是浏览器的一个安全功能,不一样源的客户端脚本在没有明确受权的状况下,不能读写对方资源。html
若是两个页面的协议,端口(若是有指定)和主机都相同,则两个页面具备相同的源。前端
JSONP的简单实现模式,或者说是JSONP的原型:建立一个回调函数,而后在远程服务上调用这个函数而且将JSON 数据形式做为参数传递,完成回调。jquery
经过script标签来实现。ajax
后端代码django
urlpatterns = [ url(r'^abc/', views.abc), ] def abc(request): return HttpResponse("rion()")
前端代码json
<!DOCTYPE HTML> <html> <head> <meta charset="UTF-8"> <meta http-equiv="x-ua-compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>xyz</title> </head> <body> <button id="b1">点我</button> <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script> <script> function rion() { console.log("盘他,盘圆润喽!"); } </script> <script src="http://127.0.0.1:8002/abc/"></script> </body> </html>
这样就能够简单实现跨域,前端打印结果为盘他,盘圆润喽!segmentfault
一样的,这种方式也是能够传参数的。后端
后端代码api
urlpatterns = [ url(r'^abc/', views.abc), ] def abc(request): res = {"code": 0, "data": ["SNIS-561", "SNIS-517", "SNIS-539"]} return HttpResponse("rion({})".format(json.dumps(res)))
前端代码
<!DOCTYPE HTML> <html> <head> <meta charset="UTF-8"> <meta http-equiv="x-ua-compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>xyz</title> </head> <body> <button id="b1">点我</button> <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script> <script> function rion(res) { console.log(res); } </script> <script src="http://127.0.0.1:8002/abc/"></script> </body> </html>
这样的话就能够实现传参的跨域。
后端代码
urlpatterns = [ url(r'^abc/', views.abc), ] def abc(request): res = {"code": 0, "data": ["SNIS-561", "SNIS-517", "SNIS-539"]} func = request.GET.get("callback") return HttpResponse("{}({})".format(func, json.dumps(res)))
前端代码
<!DOCTYPE HTML> <html> <head> <meta charset="UTF-8"> <meta http-equiv="x-ua-compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>xyz</title> </head> <body> <button id="b1">点我</button> <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script> <script> function rion(res) { console.log(res); } function addScriptTag(src){ var scriptEle = document.createElement("script"); $(scriptEle).attr("src", src); $("body").append(scriptEle); $(scriptEle).remove(); } $("#b1").click(function () { addScriptTag("http://127.0.0.1:8002/abc/") }) </script> </body> </html>
点击b1按钮的时候,会在页面上插入一个script标签,而后从后端获取数据。
后端代码
urlpatterns = [ url(r'^abc/', views.abc), ] def abc(request): res = {"code": 0, "data": ["SNIS-561", "SNIS-517", "SNIS-539"]} func = request.GET.get("callback") return HttpResponse("{}({})".format(func, json.dumps(res)))
前端代码
<!DOCTYPE HTML> <html> <head> <meta charset="UTF-8"> <meta http-equiv="x-ua-compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>xyz</title> </head> <body> <button id="b1">点我</button> <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script> <script> $("#b1").click(function () { $.getJSON("http://127.0.0.1:8002/abc/?callback=?", function (res) { console.log(res); }) }); </script> </body> </html>
要注意的是在url的后面必需要有一个callback参数,这样getJSON方法才会知道是用JSONP方式去访问服务,callback后面的那个?是jQuery内部自动生成的一个回调函数名。
若是咱们想本身指定回调函数名,或者说服务上规定了回调函数名该怎么办呢?咱们能够使用$.ajax方法来实现:
后端代码
urlpatterns = [ url(r'^abc/', views.abc), ] def abc(request): res = {"code": 0, "data": ["SNIS-561", "SNIS-517", "SNIS-539"]} func = request.GET.get("callback") return HttpResponse("{}({})".format(func, json.dumps(res)))
前端代码
<!DOCTYPE HTML> <html> <head> <meta charset="UTF-8"> <meta http-equiv="x-ua-compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>xyz</title> </head> <body> <button id="b1">点我</button> <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script> <script> $("#b1").click(function () { $.ajax({ url: "http://127.0.0.1:8002/abc/", dataType: "jsonp", jsonp: "callback", jsonpCallback: "rion2" }) }); function rion2(res) { console.log(res); } </script> </body> </html>
不过,咱们一般将回调函数鞋子啊success回调中:
<!DOCTYPE HTML> <html> <head> <meta charset="UTF-8"> <meta http-equiv="x-ua-compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>xyz</title> </head> <body> <button id="b1">点我</button> <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script> <script> $("#b1").click(function () { $.ajax({ url: "http://127.0.0.1:8002/abc/", dataType: "jsonp", success: function (res) { console.log(res); } }) }) </script> </body> </html>
CORS,跨域资源共享。CORS有两种请求,简单请求(simple request)和非简单请求(no simple request)。
只要同时知足如下两大条件,就属于简单请求。
请求方法是如下三种之一:
HTTP的头信息不超出如下几种字段:
在跨域场景下,当浏览器发送简单请求时,浏览器会自动在请求头中添加代表请求来源的 Origin 字段。
后端程序只须要在返回的响应头中加上 Access-Control-Allow-Origin 字段,而且把该字段的值设置为 跨域请求的来源地址或简单的设置为 * 就能够了。
能够在Django中间件中的process_response方法来给相应对象添加该字段。
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
对于非简单请求,浏览器一般都会在请求以前发送一次 OPTIONS 预检 请求,返回码是204,该请求会向后端服务询问是否容许从当前源发送请求而且询问容许的 请求方法 和 请求头字段。预检测经过才会真正发出请求,这才返回200。
如今前端向后端发送PUT请求,具体的发送请求以下:
解决犯法就是在后端简单的响应对象添加上经常使用的请求方法。
django代码示例以下:
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
这个是一个处理cors跨域问题的包,咱们只须要安装使用便可。
安装
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' )
若是咱们请求的时候仍是用前端的域名,而后有个东西帮咱们把这个请求转发到真正的后端域名上,这样就避免跨域,这时候,Nginx出场了。
server { # 监听9099端口 listen 9099; # 域名是localhost server_name localhost; # 凡是localhost:9099/api这样子的,都转发到真正的服务端地址http://localhost:9871 location ^~ /api { proxy_pass http://localhost:9871; } }
参看资料
https://developer.mozilla.org/zh-CN/docs/Web/Security/Same-origin_policy