跨域
什么是跨域
浏览器的同源策略规定,不同源的页面不能相互访问各自的页面内容。(协议、域名、端口号。三者完全一样被视作同源。)
DOM同源策略:禁止对不同源页面DOM进行操作。这里主要场景是iframe跨域的情况,不同域名的iframe是限制互相访问的。
XmlHttpRequest同源策略:禁止使用XHR对象向不同源的服务器地址发起HTTP请求。
所以,跨站点的dom操作与请求都被视为跨域。(a域名下访问了非a域的资源)。
为什么要禁止跨域
简单的说,禁止跨域就是为了保证:网页不会被别的网页的JS篡改,或者伪造,网站接口不会被别人滥用,一定程度上防止CSRF攻击等
AJAX同源策略。如果没有AJAX同源策略,相当危险,我们发起的每一次HTTP请求都会带上请求地址对应的cookie,那么可以做如下攻击:
用户登录了自己的银行页面 http://mybank.com,http://mybank.com向用户的cookie中添加用户标识。
用户浏览了恶意页面 http://evil.com。执行了页面中的恶意AJAX请求代码。
http://evil.com向http://mybank.com发起AJAX HTTP请求,请求会默认把http://bank.com对应cookie也同时发送过去。
银行页面从发送的cookie中提取用户标识,验证用户无误,response中返回请求数据。此时数据就泄露了。
而且由于Ajax在后台执行,用户无法感知这一过程。
DOM同源策略,如果iframe之间可以跨域访问,可以这样攻击:
做一个假网站,里面用iframe嵌套一个银行网站 http://bank.com。
把iframe宽高啥的调整到页面全部,这样用户进来除了域名,别的部分和银行的网站没有任何差别。
这时如果用户输入账号密码,我们的主网站可以跨域访问到http://bank.com的dom节点,就可以拿到用户的输入了,那么就完成了一次攻击。
// HTML
<iframe name="yinhang" src="www.bank.com"></iframe>
// JS
// 由于没有同源策略的限制,钓鱼网站可以直接拿到别的网站的Dom
iframe = window.frames['yinhang'];
name = iframe.document.getElementById('你输入账号的Input').val();
pwd = iframe.document.getElementById('你输入密码的Input').val();
所以说有了跨域跨域限制之后,我们才能更安全的上网了。但是跨域限制并不能完全解决CSRF攻击,只能在一定程度上防范,因为浏览器并未对img.src,script.src,link.href,a.href以及form.action作同源限制。详情看web安全。
什么时候会有跨域需求
1、应用的不同模块部署在不同的服务器上,模块间存在相互调用(前端)。
2、两家企业合作,A企业要调用B企业的接口(前端)。
如何实现跨域访问
1、jsonp
JSONP的最基本的原理是:动态添加一个<script>标签,而script标签的src属性是没有跨域的限制的。这样说来,这种跨域方式其实与ajax XmlHttpRequest协议无关了。
这样其实"jQuery AJAX跨域问题"就成了个伪命题,jquery $.ajax方法名有误导人之嫌。
如果设为dataType: 'jsonp',这个$.ajax方法就和ajax XmlHttpRequest没什么关系了,取而代之的则是JSONP协议。JSONP是一个非官方的协议,它允许在服务器端集成Script tags返回至客户端,通过javascript callback的形式实现跨域访问。
JSONP即JSON with Padding。由于同源策略的限制,XmlHttpRequest只允许请求当前源(域名、协议、端口)的资源。如果要进行跨域请求, 我们可以通过使用html的script标记来进行跨域请求,并在响应中返回要执行的script代码,其中可以直接使用JSON传递javascript对象。 这种跨域的通讯方式称为JSONP。
eg. 在A域上的一个html页面请求另一个域的数据
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
<script type="text/javascript">
// 得到航班信息查询结果后的回调函数
var flightHandler = function(data){
alert('你查询的航班结果是:票价 ' + data.price + ' 元,' + '余票 ' + data.tickets + ' 张。');
};
// 提供jsonp服务的url地址(不管是什么类型的地址,最终生成的返回值都是一段javascript代码)
var url = "http://flightQuery.com/jsonp/flightResult?code=CA1998&callback=flightHandler";
// 创建script标签,设置其属性
var script = document.createElement('script');
script.setAttribute('src', url);
// 把script标签加入head,此时调用开始
document.getElementsByTagName('head')[0].appendChild(script);
</script>
</head>
<body>
</body>
</html>
服务端只需要返回:
flightHandler({
"code": "CA1998",
"price": 1780,
"tickets": 5
});
例如java后台:
@Controller
public class ExchangeJsonController {
@RequestMapping("/jsonp/flightResult")
public void exchangeJson(HttpServletRequest request,HttpServletResponse response) {
try {
response.setContentType("text/plain");
response.setHeader("Pragma", "No-cache");
response.setHeader("Cache-Control", "no-cache");
response.setDateHeader("Expires", 0);
Map<String,String> map = new HashMap<String,String>();
map.put("code", "CA1998");
map.put("price", "1780");
map.put("tickets", "5");
PrintWriter out = response.getWriter();
JSONObject resultJSON = JSONObject.fromObject(map); //根据需要拼装json
String jsonpCallback = request.getParameter("jsonpCallback");//客户端请求参数
out.println(jsonpCallback+"("+resultJSON.toString()+")");//返回jsonp格式数据
out.flush();
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
不过ajax帮我们对以上的前端过程做了封装,我们只需要简单调用就可以
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head>
<title>Untitled Page</title>
<script type="text/javascript" src=jquery.min.js"></script>
<script type="text/javascript">
jQuery(document).ready(function(){
$.ajax({
type: "get",
async: false,
url: "http://flightQuery.com/jsonp/flightResult.aspx?code=CA1998",
dataType: "jsonp",
jsonp: "callback",//传递给请求处理程序或页面的,用以获得jsonp回调函数名的参数名(一般默认为:callback)
jsonpCallback:"flightHandler",//自定义的jsonp回调函数名称,默认为jQuery自动生成的随机函数名,也可以写"?",jQuery会自动为你处理数据
success: function(json){
alert('您查询到航班信息:票价: ' + json.price + ' 元,余票: ' + json.tickets + ' 张。');
},
error: function(){
alert('fail');
}
});
});
</script>
</head>
<body>
</body>
</html>
jquery在处理jsonp类型的ajax时(虽然jquery也把jsonp归入了ajax,但其实它们真的不是一回事儿),自动帮你生成回调函数并把数据取出来供success属性方法来调用。
优点:解决跨域问题
缺点:
仅支持GET请求,不支持POST请求(因为script.src本质是一个get请求呀)。
需要前后端同时支持。
同样存在CSRF攻击,例如
安全问题可参考:http://blog.knownsec.com/2015/03/jsonp_security_technic/
2. 简单的跨域
浏览器并未对imge.src,script.src,style.href及表单提交(form.action)做跨域限制。
3、通过后端请求跨域链接,通过调本域的api去请求跨域站点,跨过前端限制
4. CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing)
CORS需要浏览器和服务器同时支持。整个CORS通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS通信与同源的AJAX通信没有差别,代码完全一样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。
简单来讲就是通过浏览器在请求头中添加Orgin头指定访问源,然后在服务端验证是否允许该源访问其资源,允许则将资源返回并加上一些响应头,不允许就不带上与跨域相关的响应头。这些响应头会供浏览器判断,此次跨域请求是否成功,再我们的ajax请求中给出提示。有简单请求与非简单请求之分,详细可参考:http://www.ruanyifeng.com/blog/2016/04/cors.html
另外:
1、跨域实际是向服务端发起了请求的,只是服务端的响应被浏览器阻断了。
2、同源策略是为了防止一个源上的脚本使用其他源上的资源,比如读取其他源上的页面内容。
3、CSRF 攻击只是让浏览器发出请求,并没有使用其它源上的资源,所以也并不受同源策略的影响。
上一篇: SpringBoot访问静态资源