美文网首页程序员
web跨域请求实战

web跨域请求实战

作者: 48892085f47c | 来源:发表于2016-08-08 21:38 被阅读0次

    同源策略

    理解跨域首先必须要了解同源策略。同源策略是浏览器上为安全性考虑实施的非常重要的安全策略。何谓同源:URL由协议、域名、端口和路径组成,如果两个URL的协议、域名和端口相同,则表示他们同源。

    跨域就是通过某些手段来绕过同源策略限制,实现不同服务器之间通信的效果。具体策略限制情况可看下表:

    URL 说明
    http://www.a.com/a.js http://www.a.com/b.js 同一域名下
    http://www.a.com/lab/a.js http://www.a.com/script/b.js 同一域名不同文件夹下
    http://www.a.com:8000/a.js http://www.a.com/b.js 同一域名不同端口
    http://www.a.com/a.js https://www.a.com/b.js 同一域名不同协议
    http://www.a.com/a.js http://127.0.0.100/b.js 域名和域名对应ip
    http://www.a.com/a.js http://script.a.com/b.js 主域相同,子域不同
    http://www.a.com/a.js http://a.com/b.js 同一域名,不同二级域名
    http://www.a.com/a.js http://www.b.com/b.js 不同域名

    只有文件协议、域名、端口和路径全部相同,这些文件才会是同源,同源文件间请求无须特殊处理,当不同源文件之间发生请求时,则需要跨域处理。

    jsonp实现跨域原理

    什么是jsonp?
    参考百度百科,JSONP(JSON with Padding)是JSON的一种“使用模式”,可用于解决主流浏览器的跨域数据访问的问题。由于同源策略,一般来说位于 server1.example.com 的网页无法与不是 server1.example.com的服务器沟通,而 HTML 的<script> 元素是一个例外。利用 <script> 元素的这个开放策略,网页可以得到从其他来源动态产生的 json资料,而这种使用模式就是所谓的 jsonp。用 jsonp抓到的资料并不是 json,而是任意的JavaScript,用 JavaScript 直译器执行而不是用 json解析器解析。

    jsonp原理:
    首先在客户端注册一个callback, 然后把callback的名字传给服务器。此时,服务器先生成 json 数据。然后以 javascript 语法的方式,生成一个function , function 名字就是传递上来的参数 jsonp 。最后将 json 数据直接以入参的方式,放置到 function 中,这样就生成了一段 js 语法的文档,返回给客户端。客户端浏览器,解析script标签,并执行返回的 javascript 文档,此时数据作为参数,传入到了客户端预先定义好的 callback 函数里.(动态执行回调函数)

    jsonp作用:
    由于同源策略的限制,XmlHttpRequest只允许请求当前源(域名、协议、端口)的资源,为了实现跨域请求,可以通过script标签实现跨域请求,然后在服务端输出json数据并执行回调函数,从而解决了跨域的数据请求。

    jsonp缺点:

    1. 它只支持GET请求而不支持POST等其它类型的HTTP请求(虽然采用post+动态生成iframe是可以达到post跨域的目的,但这样做是一个比较极端的方式,不建议采用)。
    2. jsonp易于实现,但是也会存在一些安全隐患,如果第三方的脚本随意地执行,那么它就可以篡改页面内容,截获敏感数据。但是在受信任的双方传递数据,jsonp是非常合适的选择。可以看出来jsonp跨域一般用于获取其他域的数据。

    一言不合上代码

    获取json数据

    使用jsonp获取json数据,类似同源post请求获取json数据,不过jsonp只支持get请求。

    1. 客户端注册callback,并将callback名字传给服务器

    $.ajax({ url: "http://localhost:8080" + "/my/order/cancel?orderNo=" + orderNo,    
     type: "get",        
     dataType: "jsonp",    
     jsonp: 'callback',   
     jsonpCallback: 'jsonp_callback',    
     success: function (data, status) {   
        //回调处理  
        alert(data);   
       }
    });
    

    上述代码,callback是回传至服务器参数,服务器使用callback参数拼接服务器端请求结果(json数据),返回给客户端。

    *2. 服务器端处理请求 *

      @RequestMapping("/cancel")
      @ResponseBodypublic JSONPObject cancelOrder(String orderNo, HttpSession session, HttpServletRequest request, HttpServletResponse response, String callback) {   
       // 获取用户信息    
      Member member = (Member)   session.getAttribute(Constants.SESSION_MEMBER_NAME);   
      String operateIp = ClientIpUtil.getClientIp(request);    
      UserOrderOpCtx operateCtx = new UserOrderOpCtx(orderNo,String.valueOf(member.getId()),member.getNickName(),operateIp);   
       userOrderApiService.cancelOrder(operateCtx);
       response.setContentType("text/plain");    
       return new JSONPObject(callback, MapUtils.getMap(RespEnum.OK, "订单取消成功!"));   
    }
    

    根据请求客户端请求参数callback,组装jsonp数据,返回给客户端;

    获取html数据

    有些业务场景需要跨域获取其他系统页面数据,类似同源间get请求;
    1. 客户端注册callback,并将callback名字传给服务器

    $.ajax({ url: "http://localhost:8080" + "/my/order/cancel?orderNo=" + orderNo,    
     type: "get",        
     dataType: "jsonp",    
     jsonp: 'callback',   
     jsonpCallback: 'jsonp_callback',    
     success: function (data, status) {   
        //回调处理  
        alert(data);   
       }
    });
    

    上述代码,callback是回传至服务器参数,服务器使用callback参数拼接服务器端请求结果(json数据),返回给客户端。
    *2. 服务器端处理请求 *

      @RequestMapping("/cancel")
      public void cancelOrder(String orderNo, HttpSession session,HttpServletResponse response, String callback) {   
       // 获取用户信息    
      Member member = (Member)   session.getAttribute(Constants.SESSION_MEMBER_NAME);   
      String operateIp = ClientIpUtil.getClientIp(request);    
      UserOrderOpCtx operateCtx = new     UserOrderOpCtx(orderNo,String.valueOf(member.getId()),member.getNickName(),operateIp);   
      userOrderApiService.cancelOrder(operateCtx);
      response.setContentType("text/plain");    
      response.getWriter().write(callback+ "JSON.toJSON(MapUtils.getMap(RespEnum.OK, "订单取消成功!"))"); //返回jsonp数据
    }
    

    HTTP访问控制

    跨源资源共享(CROS)让Web应用服务器能支持跨站访问控制,从而使得安全地进行跨站数据传输成为可能。需要特别注意的是,这个规范是针对API容器的。比如说,要使得XMLHttpRequest在现代浏览器中可以发起跨域请求。浏览器必须能支持跨源共享带来的新的组件,包括请求头和策略执行。同样,服务器端则需要解析这些新的请求头,并按照策略返回相应的响应头以及所请求的资源。

    HTTP响应头

    这部分里列出了跨域资源共享(Cross-Origin Resource Sharing)时,服务器端需要返回的响应头信息.上一部分内容是这部分内容在实际运用中的一个概述.

    Access-Control-Allow-Origin
    返回的资源需要有一个 Access-Control-Allow-Origin 头信息,语法如下:
    Access-Control-Allow-Origin: <origin> | *
    origin参数指定一个允许向该服务器提交请求的URI.对于一个不带有credentials的请求,可以指定为'',表示允许来自所有域的请求.
    举个栗子,允许来自 http://mozilla.com 的请求,你可以这样指定:
    Access-Control-Allow-Origin: http://mozilla.com
    如果服务器端指定了域名,而不是'
    ',那么响应头的Vary值里必须包含Origin.它告诉客户端: 响应是根据请求头里的Origin的值来返回不同的内容的.

    Access-Control-Expose-Headers
    Requires Gecko 2.0(Firefox 4 / Thunderbird 3.3 / SeaMonkey 2.1)
    设置浏览器允许访问的服务器的头信息的白名单:
    Access-Control-Expose-Headers: X-My-Custom-Header, X-Another-Custom-Header
    这样, X-My-Custom-Header
    和 X-Another-Custom-Header这两个头信息,都可以被浏览器得到.

    Access-Control-Max-Age
    这个头告诉我们这次预请求的结果的有效期是多久,如下:
    Access-Control-Max-Age: <delta-seconds>
    delta-seconds
    参数表示,允许这个预请求的参数缓存的秒数,在此期间,不用发出另一条预检请求.

    Access-Control-Allow-Credentials
    告知客户端,当请求的credientials属性是true的时候,响应是否可以被得到.当它作为预请求的响应的一部分时,它用来告知实际的请求是否使用了credentials.注意,简单的GET请求不会预检,所以如果一个请求是为了得到一个带有credentials的资源,而响应里又没有Access-Control-Allow-Credentials头信息,那么说明这个响应被忽略了.
    Access-Control-Allow-Credentials: true | false

    带有credential的请求在上面讨论.
    Access-Control-Allow-Methods
    指明资源可以被请求的方式有哪些(一个或者多个). 这个响应头信息在客户端发出预检请求的时候会被返回. 上面有相关的例子.
    Access-Control-Allow-Methods: <method>[, <method>]*

    发出预检请求的例子见上,这个例子里就有向客户端发送Access-Control-Allow-Methods响应头信息.

    Access-Control-Allow-Headers
    也是在响应预检请求的时候使用.用来指明在实际的请求中,可以使用哪些自定义HTTP请求头.比如
    Access-Control-Allow-Headers: X-PINGOTHER
    这样在实际的请求里,请求头信息里就可以有这么一条:
    X-PINGOTHER: pingpong
    可以有多个自定义HTTP请求头,用逗号分隔.
    Access-Control-Allow-Headers: <field-name>[, <field-name>]*

    HTTP 请求头

    这部分内容列出来当浏览器发出跨域请求时可能用到的HTTP请求头.注意这些请求头信息都是在请求服务器的时候已经为你设置好的,当开发者使用跨域的XMLHttpRequest的时候,不需要手动的设置这些头信息.
    Origin
    表明发送请求或者预请求的域
    Origin: <origin>

    参数origin是一个URI,告诉服务器端,请求来自哪里.它不包含任何路径信息,只是服务器名.
    Note: Origin的值可以是一个空字符串,这是很有用的.
    注意,不仅仅是跨域请求,普通请求也会带有ORIGIN头信息.
    Access-Control-Request-Method
    在发出预检请求时带有这个头信息,告诉服务器在实际请求时会使用的请求方式
    Access-Control-Request-Method: <method>

    相关的例子可以在这里找到
    Access-Control-Request-Headers
    在发出预检请求时带有这个头信息,告诉服务器在实际请求时会携带的自定义头信息.如有多个,可以用逗号分开.
    Access-Control-Request-Headers: <field-name>[, <field-name>]*
    尊重版权,参考博客Ajax+Spring MVC实现跨域请求(JSONP),
    HTTP访问控制

    相关文章

      网友评论

        本文标题:web跨域请求实战

        本文链接:https://www.haomeiwen.com/subject/reeksttx.html