1.什么是同源策略
浏览器的同源策略限制从一个源加载的文档或脚本与来自另一个源的资源进行交互。
两个页面地址中的协议、域名和端口号一致,则表示同源。
例如地址 https://www.google.com 和以下地址对比
地址 | 同源 | 原因 |
---|---|---|
http://www.google.com | 否 | 协议不一致 |
https://google.com | 否 | 域名不一致 |
https://www.google.com:81 | 否 | 端口号不一致 |
https://www.google.com/a/s.html | 是 | 协议,域名和端口号都一致 |
同源策略的限制:
- 存储在浏览器中的数据,如localStroage、Cooke和IndexedDB不能通过脚本跨域访问;
- 不能通过脚本操作不同域下的DOM;
- 不能通过ajax请求不同域的数据。
2.为什么要同源策略
跨域的安全限制是针对浏览器端来说的,服务器端不存在跨域安全限制。
设置同源限制主要是为了安全,如果没有同源限制存在浏览器中的cookie等其他数据可以任意读取,不同域下DOM任意操作,Ajax任意请求的话如果浏览了恶意网站那么就会泄漏这些隐私数据。
Cookie一般用来保存登录状态。在登录一个银行网站后此时浏览器中就保存了登录的状态,同时浏览了恶意网站,这时Cookie的信息没有同源限制的话恶意网站就可以获取这些Cookie信息来达到不为人知的目的。
如果可以操作不同域下的DOM可以用如下方式完成盗取信息。在自己的网站上嵌入一个iframe地址设置成银行地址,然后让iframe全屏显示,当你一不小心上当了输入你账号密码,我就可以通过DOM操作获取到输入的信息。
Ajax的限制同Cookie,如果带上Cookie去跨域访问接口就可以通过程序的验证被认为身份是合法的。
既然瞥见危害一角自然要严加防范,限制非同源操作。
3.Ajax同源策略的规避
一个网站中的数据有时并不在同一台服务器上,这时就需要跨域调用数据,下面是ajax规避同源策略的方法。
虽然跨域限制了Ajax请求,但是却并不影响跨域引用脚本。
<script src="http://example.com/index.php?arg=val1&jsonp=callback"/>
<script>
callback(data){
console.log(data);
}
</script>
<?php
echo $_GET['jsonp'] . '(' . '数据' . ')';
?>
上面的 index.php 接口返回的是一段调用 函数的js代码 callback(data),其中data就是接口要返回的数据。而这个callback是事先在页面上写好的处理数据的回调函数,并且回调函数的名称通过URL参数的形式传递给了后端接口,这样就完成了一次跨域。
再看jquery的jsonp方式跨域请求:
服务端代码不变,js代码如下:最简单的方式,只需配置一个dataType:’jsonp’,就可以发起一个跨域请求。jsonp指定服务器返回的数据类型为jsonp格式,可以看发起的请求路径,自动带了一个callback=xxx,xxx是jquery随机生成的一个回调函数名称。
看jquery源码,可以看到jsonp调用回调函数时,是调用的window.callback。
可以通过设置参数jsonpCallback: “xxx”,指定回调函数名称。
通过设置jsonp: “xxxx”,改变callback这个名称,指定参数名称。
指定callback这个名称后,后台也需要跟着更改。
前端代码:
<html>
<head>
<title>跨域测试</title>
<script src="js/jquery-1.7.2.js"></script>
<script>
function showData (data)
console.info("调用showData");
var result = JSON.stringify(data);
$("#text").val(result);
}
$(document).ready(function () {
$("#btn").click(function () {
$.ajax({
url: "http://localhost:9090/student",
type: "GET",
dataType: "jsonp", //指定服务器返回的数据类型
jsonp: "theFunction", //指定参数名称
jsonpCallback: "showData", //指定回调函数名称
success: function (data) {
console.info("调用success");
}
});
});
});
</script>
</head>
<body>
<input id="btn" type="button" value="跨域获取数据" />
<textarea id="text" style="width: 400px; height: 100px;">
</textarea>
</body>
</html>
后端代码:
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=UTF-8");
//数据
List<Student> studentList = getStudentList();
JSONArray jsonArray = JSONArray.fromObject(studentList);
String result = jsonArray.toString();
//前端传过来的回调函数名称
String callback = request.getParameter("theFunction");
//用回调函数名称包裹返回数据,这样,返回数据就作为回调函数的参数传回去了
result = callback + "(" + result + ")";
response.getWriter().write(result);
}
注:jsonp只支持GET请求。
jQuery ajax方式以jsonp类型发起跨域请求,其原理跟script脚本请求一样,因此使用jsonp时也只能使用GET方式发起跨域请求。跨域请求需要服务端配合,设置callback,才能完成跨域请求。
上文:Vue请求数据——HTTP库axios
更多内容,欢迎关注微信公众号“让知识成为资产”。