同源策略
同源策略(Same origin policy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。可以说Web是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。
同源策略,它是由Netscape提出的一个著名的安全策略。现在所有支持JavaScript 的浏览器都会使用这个策略。所谓同源是指,域名,协议,端口相同。当一个浏览器的两个tab页中分别打开来 百度和谷歌的页面当浏览器的百度tab页执行一个脚本的时候会检查这个脚本是属于哪个页面的,即检查是否同源,只有和百度同源的脚本才会被执行。那么在请求数据时,浏览器会在控制台中报一个异常,提示拒绝访问。
例子
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>同源2</title> </head> <body> <h3>主页</h3> <button class="get_service">点我</button> <script src="/static/jquery-3.3.1.js"></script> <script> $(".get_service").click(function () { $.ajax({ url:"http://127.0.0.1:8008/service/", // 注意 我请求的是另一个服务器 success:function (data) { console.log(data); } }) }); </script>
项目二 view.py 端口 8008
from django.shortcuts import render,HttpResponse # Create your views here. def index(request): return render(request,'index.html') import json def service(request): info = {"name": "yk", "age": 20} return HttpResponse(json.dumps(info))
当我点击项目一 里的 button 按钮时,提示如下错误
已拦截跨源请求:同源策略禁止读取位于 http://127.0.0.1:8008/service/ 的远程资源。(原因:CORS 头缺少 'Access-Control-Allow-Origin')。
Jsonp
JSONP(JSON with Padding)是JSON的一种“使用模式”,可用于解决主流浏览器的跨域数据访问的问题。其核心思想是利用JS标签里面的跨域特性进行跨域数据访问,
在JS标签里面存在的是一个跨域的URL,实际执行的时候通过这个URL获得一段字符串,这段返回的字符串必须是一个合法的JS调用,通过EVAL这个字符串来完成对获得的数据的处理。
jsonp是json用来跨域的一个东西。原理是通过script标签的跨域特性来绕过同源策略
项目一 index.html 端口 8000
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>同源2</title> </head> <body> <h3>主页</h3>
<button class="get_service">点我</button> <script src="/static/jquery-3.3.1.js"></script> <script> function func(arg) { console.log(arg); console.log(typeof arg); data = JSON.parse(arg); console.log(data); console.log(typeof data); } </script> <script src="http://127.0.0.1:8008/service/"></script>
项目二 view.py 端口 8008
from django.shortcuts import render,HttpResponse # Create your views here. def index(request): return render(request,'index.html') import json def service(request): info = {"name": "yk", "age": 20} return HttpResponse("func('%s')" % json.dumps(info))
当再次刷新index.htnl
可以见到,成功访问到数据
项目一 index.html 端口 8000 点击按钮获取数据
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>同源2</title> </head> <body> <h3>主页</h3> <button class="get_service">点我</button> <script src="/static/jquery-3.3.1.js"></script> <script> function func(arg) { console.log(arg); console.log(typeof arg); data = JSON.parse(arg); console.log(data); console.log(typeof data); } </script> $(".get_service").click(function () {#} $(".get_service").click(function () { var ele_script = $("<script>"); ele_script.attr("src", "http://127.0.0.1:8008/service/"); ele_script.attr("id", "jsonp"); $("body").append(ele_script); $("#jsonp").remove();} ) </script>
jQuery对JSONP的实现
项目一 index.html 端口 8000
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>同源2</title> </head> <body> <h3>主页</h3> <button class="get_service">点我</button> <script src="/static/jquery-3.3.1.js"></script> <script> $(".get_service").click(function () { $.ajax({ url: "http://127.0.0.1:8008/service/", type: "get", dataType: "jsonp", //必须有,告诉server,这次访问要的是一个jsonp的结果。 jsonp: "callbacks", //jQuery帮助随机生成的:callbacks="jqueryxxxx"
// jsonpCallback:"SayHi" , // 如果指定 ,则回调函数名 为SayHi
success: function (data) { console.log(data) } }) }) </script> </body> </html>
项目二 view.py 端口 8008
from django.shortcuts import render,HttpResponse # Create your views here. def index(request): return render(request,'index.html') import json def service(request): func = request.GET.get("callbacks") # 获取 回调函数名 info = {"name": "yk", "age": 20} return HttpResponse("%s('%s')" % (func,json.dumps(info)))
jsonp: 'callbacks'就是定义一个存放回调函数的键,jsonpCallback是前端定义好的回调函数方法名'SayHi',server端接受callback键对应值后就可以在其中填充数据打包返回了;
jsonpCallback参数可以不定义,jquery会自动定义一个随机名发过去,那前端就得用回调函数来处理对应数据了。利用jQuery可以很方便的实现JSONP来进行跨域访问。
注意 JSONP一定是GET请求
CORS
CORS需要浏览器和服务器同时支持。目前,所有浏览器都支持该功能,IE浏览器不能低于IE10。
整个CORS通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS通信与同源的AJAX通信没有差别,
代码完全一样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。
因此,实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信。
支持跨域,简单请求
服务器设置响应头:Access-Control-Allow-Origin = '域名' 或 '*'
支持跨域,复杂请求
由于复杂请求时,首先会发送“预检”请求,如果“预检”成功,则发送真实数据。
- “预检”请求时,允许请求方式则需服务器设置响应头:Access-Control-Request-Method
- “预检”请求时,允许请求头则需服务器设置响应头:Access-Control-Request-Headers
def service(request): info={"name":"egon","age":34,"price":200} response=HttpResponse(json.dumps(info)) response["Access-Control-Allow-Origin"]="http://127.0.0.1:8008" # 设置响应头:Access-Control-Allow-Origin = '域名' 或 '*' #response["Access-Control-Allow-Origin"]="*" return response