跨域解决方案(一)

跨域问题详解

工作中我们经常会遇到ajax跨域的问题,今天我们了解一下跨域的原因和各种解决方案

跨域原因


wKioL1hjbgWiI8UAAABaLoGOrro496.png


所谓跨域就是:在a.com域下,通过ajax请求访问b.com域下的资源,出于安全的考虑,浏览器同源策略允许跨域写,而不允许跨域读,写就是上行,发送请求,send request,读就是下行,接受响应,receive response;

通俗点就是:a域向b域发送ajax请求,可以请求成功,但在请求的结果返回时,浏览器会先去判断a域与b域是否是同一个域下,“是”则返回数据,“否”则拦截数据并抛出如下异常信息

No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost' is therefore not allowed access.


解决方案


主流解决方案有

  • jsonp

  • 后台代理

  • cors

其他解决方案有

  • tomcat设置过滤器(基于cors)

  • nginx设置(基于cors)

  • chrome通过命令屏蔽跨域安全检查

  • chorme通过插件实现跨域(基于cors)

JSONP

  • 原理:

    浏览器的同源策略中虽然限制了不同域之间无法通过ajax请求得到响应结果,但有些html标签可以突破该限制,如img,srcipt,iframe标签中的src属性,可以引用不属于本域的资源且执行成功,jsonp的原理就是利用src属性的特性,动态的创建这些标签发送请求,接收响应后执行回调

  • 案例:在a域中向b域发送ajax请求

      a域:前端js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
        //定义回调函数    
        function myCallBack(data){   
             alert( "回调的数据为:" +data);
       
      
       //按钮点击事件
       function btnClick(){  
           //创建script标签  
           var script = document.createElement( 'script' );   
           //b域的资源路径   
           script.src =  'http://www.b.com/getData?callback=myCallBack' ;   
           // 在文档中追加script标签,开始发送请求到后台,响应结果后会自动调用myCallBack   
           document.body.appendChild(script);
       }

 a域:html

1
  < input  type = "button"  onclick = "btnClick"  value = "请求资源" />

b域:java后端代码 

1
2
3
4
5
6
7
     //映射请求路径    
     @RequestMapping("/getData")    
     @ResponseBody    
     public String getData(String callback){        
         //获取回调函数名后,拼接函数加数据返回        
         return callback+"('访问成功')";   
     }

  • 流程:

    1.a域前端定义回调函数和点击事件

    2.当a域用户点击按钮后,触发点击事件,创建script对象且向b域后端发送请求

    3.当b域后端接收回调函数名后,拼接结果返回

    4.执行a域前端中的回调函数


  • 优点:

    1. 环境依赖少,只需要浏览器兼容js就可以执行

    2.兼容性强,基本所有的浏览器都可以采用该方案(包括IE6,7等老式浏览器)

  • 缺点:

    1.只能执行get类型http请求,非get请求无法采用该方案

    2.需要b域后端代码配合,侵入性强,在b域后端代码不可控的情况下无法采用

  • 其他:

    jquery中的jsonp原理与上面案例类似,只是在发送请求的过程中会帮我们动态创建回调函数,在调用完成后删除回调函数减少内存消耗,当然也可以通过设置指定回调函数名称传给后台

后台代理


原理:http请求可以正常访问跨域的资源,如果a域想访问b域的资源,可以在a域中的前端发送ajax请求到a域的后端,在a域的后端中发送http请求到b域获取资源,然后将资源返回给a域前端


案例:

a域:html

 <input type="button" onclick="btnClick" value="请求资源"/>

a域:前端js

1
2
3
4
5
6
7
8
9
10
11
     < script  src = "/javascripts/jquery.js"  type = "text/javascript" ></ script >
     < script  type = "text/javascript" >
  
         //按钮点击事件
         function btnClick(){
             //发送ajax请求到a域自己的后端
             $.post("http://www.a.com/getData",function(data){
                 alert(data);
             });
         }
     </ script >

a域:后端代码(java)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
  //映射请求路径
     @RequestMapping("/getData")
     @ResponseBody
     public String getData(){
         CloseableHttpClient httpClient= null;
         String result=null;
         try {
                 httpClient= HttpClients.createDefault();
                 HttpGet getMethod=new HttpGet(new URI("http://www.b.com/getData"));
                 //请求b域资源 得到响应对象
                 HttpResponse response= httpClient.execute(getMethod);
                 result= EntityUtils.toString(response.getEntity(), "utf-8");
         } catch (IOException e) {
             e.printStackTrace();
         }catch (Exception e){
             e.printStackTrace();
         }finally {
             if(httpClient!=null){
                 try {
                     httpClient.close();
                 } catch (IOException e) {
                     e.printStackTrace();
                 }
             }
             //将b域资源返回给a域
             return result;
         }
     }

 b域:后端代码(java)

1
2
3
4
5
6
7
8
9
     //映射请求路径
     @RequestMapping("/getData")
     @ResponseBody
     public String getData(){
         /**
          * 省略业务逻辑代码
          */
        return "success";
     }


  • 流程

    1.用户在a域点击按钮,触发点击事件,向a域后端发送ajax请求

    2.a域后端发送http请求到b域后端请求资源

    3.请求b域资源成功后将结果返回a域前端,a域前端输出资源

  • 优点:

    1.可以使用getpost等多种请求方式

    2.b域后端不用配合a域做返回结果的修改

  • 缺点:

    1.性能较低,a域每一次请求b域资源,本质会发送2次请求,1次是发送ajax请求到a域自己后端,1次是a域后端发送请求到b域

    2.代码复杂,不利于维护。当a域b域请求资源比较多的情况下,每一个请求都要写一个后端代理,在代码量和维护上都是一个考验,该方案只适合跨域请求资源少,且b域不可控(即无法控制b域后端拼接结果返回)的情况下使用


本文转自 兴趣e族 51CTO博客,原文链接:http://blog.51cto.com/simplelife/1886821
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值