同源策略(Same origin policy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。可以说Web是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。
同源策略,它是由Netscape提出的一个著名的安全策略。现在所有支持JavaScript 的浏览器都会使用这个策略。所谓同源是指,域名,协议,端口相同。当一个浏览器的两个tab页中分别打开来 百度和谷歌的页面当浏览器的百度tab页执行一个脚本的时候会检查这个脚本是属于哪个页面的,即检查是否同源,只有和百度同源的脚本才会被执行。如果非同源,那么在请求数据时,浏览器会在控制台中报一个异常,提示拒绝访问。
1、先来说说什么是源
• 源(origin)就是指的协议、域名和端口号。
以上url中的源就是:http://www.company.com:80
若地址里面的协议、域名和端口号均相同则属于同源。
以下是相对于 http://www.a.com/test/index.html 的同源检测
• http://www.a.com/dir/page.html ----成功
• http://www.child.a.com/test/index.html ----失败,域名不同
• https://www.a.com/test/index.html ----失败,协议不同
• http://www.a.com:8080/test/index.html ----失败,端口号不同
2.什么是同源策略?
同源策略是浏览器的一个安全功能,不同源的客户端脚本在没有明确授权的情况下,不能读写对方资源。所以a.com下的js脚本采用ajax读取b.com里面的文件数据是会报错的。
• 不受同源策略限制的:
1、页面中的链接,重定向以及表单提交是不会受到同源策略限制的。
2、跨域资源的引入是可以的。但是js不能读写加载的内容。如嵌入到页面中的<script src="..."></script>,<img>,<link>,<iframe>等。
二、跨域
1、什么是跨域
受前面所讲的浏览器同源策略的影响,不是同源的脚本不能操作其他源下面的对象。想要操作另一个源下的对象是就需要跨域。
2、跨域的实现形式
• 降域 document.domain
同源策略认为域和子域属于不同的域,如:
child1.a.com 与 a.com,
child1.a.com 与 child2.a.com,
xxx.child1.a.com 与 child1.a.com
两两不同源,可以通过设置 document.damain='a.com',浏览器就会认为它们都是同一个源。想要实现以上任意两个页面之间的通信,两个页面必须都设置documen.damain='a.com'。
此方式的特点:
1. 只能在父域名与子域名之间使用,且将 xxx.child1.a.com域名设置为a.com后,不能再设置成child1.a.com。
2. 存在安全性问题,当一个站点被攻击后,另一个站点会引起安全漏洞。
3. 这种方法只适用于 Cookie 和 iframe 窗口。
• JSONP跨域
JSONP和JSON并没有什么关系!
JSONP的原理:(举例: http://127.0.0.1:8001/想得到 http://127.0.0.1:8002/中的数据)在a.com的jsonp.html里创建一个回调函数xxx,动态添加<script>元素,向服务器发送请求,请求地址后面加上查询字符串,通过callback参数指定回调函数的名字。请求地址为 http://127.0.0.1:8001?callback=xxx。在main.js中调用这个回调函数xxx,并且以JSON数据形式作为参数传递,完成回调。
现在我们看看 http://127.0.0.1:8001/的html代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>首页</title>
</head>
<body>
<p>这是首页!</p>
<button>提交</button>
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
<script>
$("button").click(function () {
alert(123)
$.ajax({
url: "http://127.0.0.1:8002//test/",
success: function (data) {
console.log(data)
}
})
})
</script>
</body>
</html>
在这个代码里面调用了8002的资源。
8802:的views代码:
from django.shortcuts import render, HttpResponse
# Create your views here.
def test(request):
print("会出现吗。很是期待哦")
return HttpResponse("this is test")
这是一个简单的调用。你会发现。浏览器不允许你进行调用。
因为控制台已经给你调用了
说明是浏览器不允许你进行跨域.
但是你们发现没有。同样是跨域调用。我进行jquery的cdn调用就不会进行拦截。
看到这里你的心里难道就没有点想法吗?
没有,既然不给我进行js的跨域,那我跨域伪装成上面的那种方式进行调用呀!说干就干,撸起手就是干。
8001的HTML代码如图:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>首页</title>
</head>
<body>
<p>这是首页!</p>
{#{% csrf_token %}#}
<button>提交</button>
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
<script>
$("button").click(function () {
var html_script = $("<script>");
html_script.attr("src","http://127.0.0.1:8002/test/");
html_script.attr("id","test");
$("body").append(html_script);
$("#test").remove();//生成的标签调用完毕,马上移除掉。
})
function get_data(test2_data) {
console.log(test2_data)
}
</script>
</body>
</html>
8002 的views代码:
from django.shortcuts import render, HttpResponse
# Create your views here.
def test(request):
print("会出现吗。很是期待哦")
return HttpResponse("get_data('ok')") # 传递一个跟8001同名的get_data()函数
这样调用跨域的资源就不会出错了。
。现在为了更加灵活,现在将在客户端定义的回调函数传送给服务端。服务端就会返回逸直接定义的回调函数名的方法。将获取的json数据传入这个方法就可以完成回调了。
8001代码也就是客户端代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>首页</title>
</head>
<body>
<p>这是首页!</p>
{#{% csrf_token %}#}
<button>提交</button>
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
<script>
$("button").click(function () {
var html_script = $("<script>");
html_script.attr("src","http://127.0.0.1:8002/test/?callbacks=get_data");
html_script.attr("id","test");
$("body").append(html_script);
$("#test").remove();//生成的标签调用完毕,马上移除掉。
})
function get_data(test2_data) {
console.log(test2_data)
}
</script>
</body>
</html>
8002也就是服务端只需这样调用即可:
from django.shortcuts import render, HttpResponse
import json
# Create your views here.
def test(request):
print("会出现吗。很是期待哦")
func = request.GET.get("callbacks")
print(func)
a_dict = {"key": "values"}
return HttpResponse("%s(%s)" % (func, json.dumps(a_dict))) # 需要把数据进行序列化方可以传递过去
让我们来看看结果呗
这就跨域调用数据成功了。
jQuery中的Jsonp方法
只需要改动8001中的html代码即可:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>首页</title>
</head>
<body>
<p>这是首页!</p>
{#{% csrf_token %}#}
<button>提交</button>
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
<script>
$("button").click(function () {
f("http://127.0.0.1:8002/test/")
})
function f(url) {
$.ajax({
url: url,
dataType:"jsonp",
jsonp: 'callbacks', //这里的值(callbacks)相当于url中的建的名字
jsonpCallback: 'ok', // 这里的值(ok)相当于回调函数的函数名,也就是url中的值
success:function (data) {
console.log(data)
}
});
}
</script>
</body>
</html>
你可以正常的跨域调用。
但是上面的代码有个可以不用有:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>首页</title>
</head>
<body>
<p>这是首页!</p>
{#{% csrf_token %}#}
<button>提交</button>
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
<script>
$("button").click(function () {
f("http://127.0.0.1:8002/test/")
})
function f(url) {
$.ajax({
url: url,
dataType:"jsonp",
jsonp: 'callbacks', //这里的值(callbacks)相当于url中的建的名字
//jsonpCallback: 'ok', // 这里的值(ok)相当于回调函数的函数名,也就是url中的值
success:function (data) {
console.log(data)
}
});
}
</script>
</body>
</html>
从这里可以看出不一定需要自己定义回调函数名也可以,jsonp也可以帮你生成函数名: