热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

构建publicAPIs与CORS

在构建PublicAPIs的过程中,首先要解决的第一个问题就是跨域请求的问题。网络应用安全模型中很重要的一个概念是“同源准则”(same-originpolicy)。该准则要求一个

在构建Public APIs的过程中,首先要解决的第一个问题就是跨域请求的问题。

网络应用安全模型中很重要的一个概念是“同源准则”(same-origin policy)。该准则要求一个网站(由协议+主机名+端口号三者确定)的脚本(Script)、XMLHttpRequest和Websocket无权去访问另一个网站的内容。在未正确设置的情况下,跨域访问会提示如下错误:No ‘Access-Control-Allow-Origin’ header is present on the requested resource. 这项限制对于跨域的Ajax请求带来了很多不便。

典型的对于跨域请求的解决方案如下:

  • document.domain property
  • Cross-Origin Resource Sharing (CORS)
  • Cross-document messaging
  • JSONP

本文重点讲述的则是其中Cross-Origin Resource Sharing (CORS)的原理和在rails下的配置方式

Cross-Origin Resource Sharing (CORS)

CORS的基本原理是通过设置HTTP请求和返回中header,告知浏览器该请求是合法的。这涉及到服务器端和浏览器端双方的设置:请求的发起(Http Request Header)和服务器对请求正确的响应(Http response header)。

发起CORS请求

CORS兼容以下浏览器:

  • Internet Explorer 8+
  • Firefox 3.5+
  • Safari 4+
  • Chrome

原生Javascript可以通过XMLHttpRequest Object或XDomainRequest发起请求,详细的方式可以参见这篇文章:http://www.html5rocks.com/en/tutorials/cors/

JQuery的$.ajax()可以用来发起XHR或者CORS请求。然而该方法不支持IE下的XDomainRequest,需要使用JQuery的插件来实现IE下的兼容性(http://bugs.jquery.com/ticket/8283)

$.ajax({
// The 'type' property sets the HTTP method.
// A value of 'PUT' or 'DELETE' will trigger a preflight request.
type: 'GET',
// The URL to make the request to.
url: 'http://updates.html5rocks.com',
// The 'contentType' property sets the 'Content-Type' header.
// The JQuery default for this property is
// 'application/x-www-form-urlencoded; charset=UTF-8', which does not trigger
// a preflight. If you set this value to anything other than
// application/x-www-form-urlencoded, multipart/form-data, or text/plain,
// you will trigger a preflight request.
contentType: 'text/plain',
xhrFields: {
// The 'xhrFields' property sets additional fields on the XMLHttpRequest.
// This can be used to set the 'withCredentials' property.
// Set the value to 'true' if you'd like to pass COOKIEs to the server.
// If this is enabled, your server must respond with the header
// 'Access-Control-Allow-Credentials: true'.
withCredentials: false
},
headers: {
// Set any custom headers here.
// If you set any non-simple headers, your server must include these
// headers in the 'Access-Control-Allow-Headers' response header.
},
success: function() {
// Here's where you handle a successful response.
},
error: function() {
// Here's where you handle an error response.
// Note that if the error was due to a CORS issue,
// this function will still fire, but there won't be any additional
// information about the error.
}
});

服务器正确响应CORS请求

根据请求内容的不同,浏览器会需要添加对应的Header或者发起额外的请求。其中的细节都由浏览器负责处理,对于用户来讲是透明的。我们只需要了解如何针对差异的请求做出适当的响应即可。
我们将CORS请求分成以下两种类型:
1、简单请求
2、不是那么简单的请求

其中简单请求要求:
请求类型必须是GET,POST,HEAD三者中的一种
请求头(Header)中仅可以包含:

  • Accept
  • Accept Language
  • Content Language
  • Last Event ID
  • Content Type:仅接受application/x-www-form-urlencoded,multipart/form-data,text/plain

不满足上述条件的所有请求,例如PUT,DELETE或者是Content Type是application/json,均为“不是那么简单的请求”。针对这种请求,浏览器会在真实请求前,额外发起一次类型为OPTIONS的请求(Preflight request),只有服务器正确响应了OPTIONS请求后,浏览器才会发起该请求。(参见下图)

《构建public APIs与CORS》

下文将针对b.com向a.com发起跨域请求说明服务器如何正确响应这两种类型的请求。

简单请求

浏览器在发出请求前为请求添加Origin来标明请求的来源,用户不可更改此内容。但Header中是否有Origin并不能作为判断是否是CORS请求的标准,因为不同浏览器对于此内容的处理方式并不完全一致,同源请求中也有可能出现Origin。
下面是一个b.com向a.com发起的一次GET请求。

GET /cors HTTP/1.1
Origin: http://b.com
Host: a.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...

正确响应的返回如下,均由Access-Control-*开头:

Access-Control-Allow-Origin: http://b.com
Access-Control-Allow-Credentials: true
Access-Control-Expose-Headers: FooBar
Content-Type: text/html; charset=utf-8

Access-Control-Allow-origin: 此处是Server同意跨域访问的域名列表。如果允许任意网站请求资源,此处可以写为’*’
Access-Control-Expose-Headers: 可以设置返回的Header以传递数据。简单请求中允许使用的Header包括:Cache-Control,Content-Language,Content-Type,Expires,Last-Modified,Pragma。

不是那么简单的请求

如果希望使用PUT,DELETE等RESTful等超出了简单请求的范围的请求,浏览器则会在发起真实请求前先向服务器发起一次称作Preflight的OPTIONS的请求,以确保服务器接受该类型请求。其后才会发起真实要求的请求。请求的发起与简单请求并无差异,而服务器端则要针对Preflight Request做额外的响应。
下面是一次典型的Preflight请求:

OPTIONS /cors HTTP/1.1
Origin: http://b.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: X-Custom-Header
Host: a.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...

Access-Control-Request-Method代表真实请求的类型。Access-Control-Request-Headers则代表真实请求的请求头key内容。服务器仅在验证了这两项内容的合法性之后才会同意浏览器发起真实的请求。

Access-Control-Allow-Origin: http://b.com
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: X-Custom-Header
Content-Type: text/html; charset=utf-8

此处并未列举的一项返回头是Access-Control-Max-Age。因为每次请求均要发起一次额外的OPTIONS请求是非常低效的,因此可以为浏览器保存该返回头设置一个缓存的时间,单位为秒。在缓存过期以前,浏览器无需再次验证同一类型的请求是否合法。

真实请求的内容则和简单请求的内容完全一致,此处不再赘述。

下图非常详细的再次描述了服务器对于不同类型的请求如何做出正确的响应。
《构建public APIs与CORS》

Rails下对CORS请求的配置

首先要确保在Routes.rb中加上对于OPTIONS请求的正确响应。
OPTIONS请求会发至真实请求的同一位置。如果未正确设置route,则会出现404无法找到请求地址的错误。
响应该请求的Controller的action方法可以设置为空,因为该请求的关键仅是正确返回请求头。
例如:真实请求/api/trips PUT,OPTIONS请求将发送至/api/trips OPTIONS。
match ‘/trips’, to: ‘trips#index’, via: [:options]
或者可以使用:
match ‘*all’ => ‘application#cor’, :cOnstraints=> {:method => ‘OPTIONS’}

确保了OPTIONS请求可以正确被响应之后,在applicationController.rb中如下配置:

before_filter :cors_preflight_check
after_filter :cors_set_access_control_headers
def cors_set_access_control_headers
headers['Access-Control-Allow-Origin'] = '*'
headers['Access-Control-Allow-Methods'] = 'POST, GET, PUT, DELETE, OPTIONS'
headers['Access-Control-Max-Age'] = '1728000'
end
def cors_preflight_check
if request.method == 'OPTIONS'
headers['Access-Control-Allow-Origin'] = '*'
headers['Access-Control-Allow-Methods'] = 'POST, PUT, DELETE, GET, OPTIONS'
headers['Access-Control-Request-Method'] = '*'
headers['Access-Control-Allow-Headers'] = 'Origin, X-Requested-With, Content-Type, Accept, Authorization'
headers['Access-Control-Max-Age'] = '1728000'
render :text => '', :content_type => 'text/plain'
end
end

对于简单请求,由cors_set_access_control_headers做出正确的响应。对于不是那么简单的请求,cors_preflight_check则会发现若请求是OPTIONS的时候,在实际执行cors_set_access_control_headers之前,拦截下该请求并返回text/plain的内容和正确的请求头。

总结

CORS请求作为构建Public API中很重要的一环,理解其大致的工作原理还是非常有意义的。不过在Chrome中,时常会出现provision header shown这样奇怪的错误,而这个错误出现的原因说法不一,基本上可以理解为跨域访问过程中如果请求出现问题chrome并没有办法很好的了解错误原因,无法准确的给出错误状态。另外,CORS调试也是一个问题,基于非浏览器的POSTMAN调试,有时不能够准确的反映出请求在浏览器下的真实工作状态,不知道如何才能更有效果的测试和调试CORS。
有兴趣的话,还可以进一步通过https://dvcs.w3.org/hg/cors/raw-file/tip/Overview.html了解W3C的CORS协议内容。http://arunranga.com/examples/access-control/

Reference:
http://en.wikipedia.org/wiki/Same_origin_policy
http://www.html5rocks.com/en/tutorials/cors/
http://www.nczonline.net/blog/2010/05/25/cross-domain-ajax-with-cross-origin-resource-sharing/
http://www.tsheffler.com/blog/?p=428
http://blog.rudylee.com/2013/10/29/rails-4-cors/
http://stackoverflow.com/questions/17858178/allow-anything-through-cors-policy


推荐阅读
  • WebSocket与Socket.io的理解
    WebSocketprotocol是HTML5一种新的协议。它的最大特点就是,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话,属于服务器推送 ... [详细]
  • 本文介绍了高校天文共享平台的开发过程中的思考和规划。该平台旨在为高校学生提供天象预报、科普知识、观测活动、图片分享等功能。文章分析了项目的技术栈选择、网站前端布局、业务流程、数据库结构等方面,并总结了项目存在的问题,如前后端未分离、代码混乱等。作者表示希望通过记录和规划,能够理清思路,进一步完善该平台。 ... [详细]
  • Python瓦片图下载、合并、绘图、标记的代码示例
    本文提供了Python瓦片图下载、合并、绘图、标记的代码示例,包括下载代码、多线程下载、图像处理等功能。通过参考geoserver,使用PIL、cv2、numpy、gdal、osr等库实现了瓦片图的下载、合并、绘图和标记功能。代码示例详细介绍了各个功能的实现方法,供读者参考使用。 ... [详细]
  • 本文介绍了NetCore WebAPI开发的探索过程,包括新建项目、运行接口获取数据、跨平台部署等。同时还提供了客户端访问代码示例,包括Post函数、服务器post地址、api参数等。详细讲解了部署模式选择、框架依赖和独立部署的区别,以及在Windows和Linux平台上的部署方法。 ... [详细]
  • Allegro总结:1.防焊层(SolderMask):又称绿油层,PCB非布线层,用于制成丝网印板,将不需要焊接的地方涂上防焊剂.在防焊层上预留的焊盘大小要比实际的焊盘大一些,其差值一般 ... [详细]
  • Linux服务器密码过期策略、登录次数限制、私钥登录等配置方法
    本文介绍了在Linux服务器上进行密码过期策略、登录次数限制、私钥登录等配置的方法。通过修改配置文件中的参数,可以设置密码的有效期、最小间隔时间、最小长度,并在密码过期前进行提示。同时还介绍了如何进行公钥登录和修改默认账户用户名的操作。详细步骤和注意事项可参考本文内容。 ... [详细]
  • 开发笔记:计网局域网:NAT 是如何工作的?
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了计网-局域网:NAT是如何工作的?相关的知识,希望对你有一定的参考价值。 ... [详细]
  • 使用正则表达式爬取36Kr网站首页新闻的操作步骤和代码示例
    本文介绍了使用正则表达式来爬取36Kr网站首页所有新闻的操作步骤和代码示例。通过访问网站、查找关键词、编写代码等步骤,可以获取到网站首页的新闻数据。代码示例使用Python编写,并使用正则表达式来提取所需的数据。详细的操作步骤和代码示例可以参考本文内容。 ... [详细]
  • 本文介绍了前端人员必须知道的三个问题,即前端都做哪些事、前端都需要哪些技术,以及前端的发展阶段。初级阶段包括HTML、CSS、JavaScript和jQuery的基础知识。进阶阶段涵盖了面向对象编程、响应式设计、Ajax、HTML5等新兴技术。高级阶段包括架构基础、模块化开发、预编译和前沿规范等内容。此外,还介绍了一些后端服务,如Node.js。 ... [详细]
  • 欢乐的票圈重构之旅——RecyclerView的头尾布局增加
    项目重构的Git地址:https:github.comrazerdpFriendCircletreemain-dev项目同步更新的文集:http:www.jianshu.comno ... [详细]
  • 超级简单加解密工具的方案和功能
    本文介绍了一个超级简单的加解密工具的方案和功能。该工具可以读取文件头,并根据特定长度进行加密,加密后将加密部分写入源文件。同时,该工具也支持解密操作。加密和解密过程是可逆的。本文还提到了一些相关的功能和使用方法,并给出了Python代码示例。 ... [详细]
  • 本文介绍了Java后台Jsonp处理方法及其应用场景。首先解释了Jsonp是一个非官方的协议,它允许在服务器端通过Script tags返回至客户端,并通过javascript callback的形式实现跨域访问。然后介绍了JSON系统开发方法,它是一种面向数据结构的分析和设计方法,以活动为中心,将一连串的活动顺序组合成一个完整的工作进程。接着给出了一个客户端示例代码,使用了jQuery的ajax方法请求一个Jsonp数据。 ... [详细]
  • XMLhttpREquest_Ajax技术总结之XmlHttpRequest
    Ajax1、 什么是ajax   ... [详细]
  • 1、概述首先和大家一起回顾一下Java消息服务,在我之前的博客《Java消息队列-JMS概述》中,我为大家分析了:然后在另一篇博客《Java消息队列-ActiveMq实战》中 ... [详细]
  • jqueryajax怎么通过header传递参数?
    /这个是全局的ajax请求头设置,所有的ajax请求都会加上这个请求头 ... [详细]
author-avatar
徐韵茜YUNXI
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有