欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

跨域

程序员文章站 2022-07-10 18:04:39
...

什么是跨域

浏览器的同源策略规定,不同源的页面不能相互访问各自的页面内容。(协议、域名、端口号。三者完全一样被视作同源。)

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 攻击只是让浏览器发出请求,并没有使用其它源上的资源,所以也并不受同源策略的影响。