本页面适用于 Apigee 和 Apigee Hybrid。
查看 Apigee Edge 文档。
CORS(跨源资源共享)是一种标准机制,允许在网页中执行的 JavaScript XMLHttpRequest (XHR) 调用与来自非源网域的资源进行交互。CORS 是通常针对所有浏览器强制执行的同源政策实现的解决方案。例如,如果您从浏览器执行的 JavaScript 代码向 Twitter API 发出 XHR 调用,则该调用将失败。这是因为为浏览器提供网页的网域与为 Twitter API 提供服务的网域不同。CORS 针对这个问题提供了一个解决方案,允许服务器在希望提供跨源资源共享的情况下选择加入。
CORS 的典型用例
以下 JQuery 代码会调用虚构的目标服务。如果在浏览器(网页)的上下文内执行,则调用将因同域政策而失败:
<script> var url = "http://service.example.com"; $(document).ready(function(){ $("button").click(function(){ $.ajax({ type:"GET", url:url, async:true, dataType: "json", success: function(json) { // Parse the response. // Do other things. }, error: function(xhr, status, err) { // This is where we end up! } }); }); }); </script>
此问题的一种解决方案是创建一个 Apigee API 代理,以便在后端调用服务 API。请记住,Apigee 位于客户端(本例中为浏览器)与后端 API(服务)之间。由于 API 代理在服务器上执行,而不是在浏览器中执行,因此等于能够成功调用服务。然后,只需将 CORS 标头附加到 TargetEndpoint 响应即可。只要浏览器支持 CORS,这些标头就会向浏览器表明可以放宽其同源政策,从而允许跨源 API 调用取得成功。
创建支持 CORS 的代理后,您可以在客户端代码中调用 API 代理网址,而不是后端服务。例如:
<script> var url = "http://myorg-test.apigee.net/v1/example"; $(document).ready(function(){ $("button").click(function(){ $.ajax({ type:"GET", url:url, async:true, dataType: "json", success: function(json) { // Parse the response. // Do other things. }, error: function(xhr, status, err) { // This time, we do not end up here! } }); }); }); </script>
将 CORS 政策附加到 ProxyEndpoint 的请求 PreFlow
将 CORS 政策附加到新的 API 代理
您可以通过以下方式向 API 代理附加添加 CORS 政策,从而为 API 代理添加 CORS 支持:
- 创建政策时,在构建代理向导的安全页面中选中添加 CORS 标头复选框
- 在添加政策对话框中选择稍后添加
通过选中此复选框添加 CORS 政策后,名为添加 CORS (Add CORS) 的政策会自动添加到系统中,并附加到 TargetEndpoint 请求 PreFlow。
添加 CORS (Add CORS) 政策会将相应的标头添加到响应中。基本上,标头可以告知浏览器它将与哪些域共享资源、它接受的方法等。如需详细了解这些 CORS 标头,请参阅跨域资源共享 W3C 建议。
您应按如下方式修改政策:
- 将
content-type
和authorization
标头(支持基本身份验证或 OAuth2 时需要)添加到Access-Control-Allow-Headers
标头中,如以下代码段所示。 - 对于 OAuth2 身份验证,您可能需要采取一些措施来纠正不符合 RFC 标准的行为。
<CORS continueOnError="false" enabled="true" name="add-cors"> <DisplayName>Add CORS</DisplayName> <AllowOrigins>{request.header.origin}</AllowOrigins> <AllowMethods>GET, PUT, POST, DELETE</AllowMethods> <AllowHeaders>origin, x-requested-with, accept, content-type, authorization</AllowHeaders> <ExposeHeaders>*</ExposeHeaders> <MaxAge>3628800</MaxAge> <AllowCredentials>false</AllowCredentials> <GeneratePreflightResponse>true</GeneratePreflightResponse> <IgnoreUnresolvedVariables>true</IgnoreUnresolvedVariables> </CORS>
将 CORS 标头添加到现有代理
新版代理编辑器
如需将 CORS 政策添加到现有 API 代理,请执行以下操作:
如果您使用的是 Cloud 控制台中的 Apigee 界面:选择代理开发 > API 代理。
如果您使用的是经典版 Apigee 界面:请选择开发 > API 代理,然后在代理窗格中,选择代理的环境。
- 选择要向其添加 CORS 政策的 API 代理。
- 在新 API 代理的编辑器中,点击开发标签页。
- 在左侧窗格中,点击政策行中的 + 按钮。
在创建政策对话框中,点击选择政策类型 (Select policy type) 字段,向下滚动到安全,然后选择 CORS。
输入政策的详细信息,然后点击创建。
- 在左侧窗格中,点击PreFlow下的 PreFlow。
- 点击可视化编辑器右下角PreFlow窗格中 PreFlow 旁边的 + 按钮:
- 在添加政策步骤 (Add policy step) 对话框中,选择 CORS 政策。
点击添加以附加该政策。
经典版代理编辑器
如需将 CORS 政策添加到现有 API 代理,请执行以下操作:
- 登录 Apigee 界面。
- 在左侧导航栏中,选择开发 > API 代理。 如果您看到立即试用按钮,请点击该按钮以显示新版开发视图。
- 选择要向其添加 CORS 政策的 API 代理。
- 在新 API 代理的编辑器中,点击开发标签页:
- 在左侧“导航器”窗格中,点击PreFlow下的 PreFlow。
- 点击与请求 PreFlow 相对应的 +步骤按钮。此时会显示您可以创建的所有政策的分类列表。
- 在CORS类别中选择 CORS。
- 指定名称(例如
Add CORS
),然后点击添加。
开发视图如下所示。
处理 CORS 预检请求
CORS 预检是指向服务器发送请求,以验证其是否支持 CORS。典型的预检响应包括服务器将从中接受 CORS 请求的域、CORS 请求支持的 HTTP 方法的列表、可以用作资源请求一部分的标头、将缓存预检响应的最长时间,等等。如果服务没有指明 CORS 支持,或者不希望接受来自客户端域的跨域请求,系统将强制执行浏览器的跨域政策,并且从客户端发出、与该服务器上托管的资源进行互动的任何跨网域请求都将失败。
CORS 预检请求通常使用 HTTP OPTIONS 方法发出。支持 CORS 的服务器收到 OPTIONS 请求后,它会向客户端返回一组 CORS 标头,以指示其 CORS 支持级别。由于存在此握手,客户端知道允许从非域网域请求的内容。
如需详细了解预检,请参阅跨域资源共享 W3C 建议。此外,您还可以参阅关于 CORS 的众多博客和文章。
Apigee 没有开箱即用的 CORS 预检解决方案,但可以按此部分所述实现。目标是让代理评估条件流中的 OPTIONS 请求。然后,代理可以将相应的响应发送回客户端。
我们来看一个示例流,然后讨论处理预检请求的各个部分:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <ProxyEndpoint name="default"> <Description/> <Flows> <Flow name="OptionsPreFlight"> <Request> <Step> <Name>add-cors</Name> </Step> </Request> <Response/> <Condition>request.verb == "OPTIONS" AND request.header.origin != null AND request.header.Access-Control-Request-Method != null</Condition> </Flow> </Flows> <PreFlow name="PreFlow"> <Request/> <Response/> </PreFlow> <HTTPProxyConnection> <BasePath>/v1/cnc</BasePath> <VirtualHost>default</VirtualHost> <VirtualHost>secure</VirtualHost> </HTTPProxyConnection> <RouteRule name="NoRoute"> <Condition>request.verb == "OPTIONS" AND request.header.origin != null AND request.header.Access-Control-Request-Method != null</Condition> </RouteRule> <RouteRule name="default"> <TargetEndpoint>default</TargetEndpoint> </RouteRule> <PostFlow name="PostFlow"> <Request/> <Response/> </PostFlow> </ProxyEndpoint>
此 ProxyEndpoint 的关键部分如下所示:
- 系统会针对 NULL 目标创建 RouteRule,其中包含 OPTIONS 请求的条件。请注意,没有指定 TargetEndpoint。如果收到 OPTIONS 请求,并且 Origin 和 Access-Control-Request-Method 请求标头不为 null,则代理会立即返回 CORS 标头以响应客户端(绕过实际默认“后端”目标)。如需详细了解流条件和 RouteRule,请参阅包含流变量的条件。
<RouteRule name="NoRoute"> <Condition>request.verb == "OPTIONS" AND request.header.origin != null AND request.header.Access-Control-Request-Method != null</Condition> </RouteRule>
- 系统将创建一个 OptionsPreFlight 流,以便在收到 OPTIONS 请求且 Origin 和 Access-Control-Control-Request-Method 请求标头不为 null 时,向该流添加“添加 CORS”政策(其中包含 CORS 标头)。
<Flow name="OptionsPreFlight"> <Request> <Step> <Name>add-cors</Name> </Step> </Request> <Response/> <Condition>request.verb == "OPTIONS" AND request.header.origin != null AND request.header.Access-Control-Request-Method != null</Condition> </Flow>