文章目录

第七章 在 REST 服务中支持 CORS

概述

本节提供 ​​CORS​​​ 的概述以及如何在 ​​IRIS REST​​​ 服务中启用 ​​CORS​​ 的概述。

CORS 简介

跨域资源共享 (​​CORS​​) 允许在另一个域中运行的脚本访问服务。

通常,当浏览器从一个域运行脚本时,它允许对同一个域进行 ​​XMLHttpRequest​​ 调用,但在对另一个域进行调用时不允许它们。此浏览器行为限制某人创建可滥用机密数据的恶意脚本。恶意脚本可能允许用户使用授予用户的权限访问另一个域中的信息,但随后在用户不知道的情况下,将机密信息用于其他用途。为了避免这种安全问题,浏览器一般不允许这种跨域调用。

在不使用跨域资源共享 (​​CORS​​​) 的情况下,具有访问 ​​REST​​​ 服务的脚本的网页通常必须与提供 ​​REST​​​ 服务的服务器位于同一域中。在某些环境中,将带有脚本的网页与提供 REST 服务的服务器放在不同的域中是很有用的。 ​​CORS​​ 支持这种安排。

下面提供了浏览器如何使用 ​​CORS​​​ 处理 ​​XMLHttpRequest​​ 的简化描述:

  1. 域​​DomOne​​​ 中的网页中的脚本包含对​​DomTwo​​​ 域中的​​IRIS REST​​​ 服务的​​XMLHttpRequest​​​。​​XMLHttpRequest​​​ 具有​​CORS​​ 的自定义标头。
  2. 用户查看此网页并运行脚本。用户的浏览器检测到与包含网页的域不同的域的​​XMLHttpRequest​​。
  3. 用户的浏览器向​​IRIS REST​​​ 服务发送一个特殊请求,该请求指示​​XMLHttpRequest​​​ 的​​HTTP​​​ 请求方法和原始网页的域,在本示例中为​​DomOne​​。
  4. 如果请求被允许,则响应包含请求的信息。否则,响应仅包含指示​​CORS​​ 不允许请求的标头。

启用 REST 服务以支持 CORS 的概述

默认情况下,​​REST​​​ 服务不允许 ​​CORS​​​ 标头。但是,可以启用 ​​CORS​​​ 支持。在 ​​REST​​​ 服务中启用对 ​​CORS​​ 的支持有两个部分:

  • 启用​​REST​​​ 服务以接受部分或所有​​HTTP​​​ 请求的​​CORS​​ 标头。。
  • 编写代码,使​​REST​​​ 服务检查​​CORS​​​ 请求并决定是否继续。例如,可以提供一个允许列表,其中包含仅包含受信任脚本的域。​​IRIS​​​ 为文档目的提供了一个简单的默认实现;此默认实现允许任何​​CORS​​ 请求。

重要提示:默认 ​​CORS​​​ 标头处理不适用于处理机密数据的 ​​REST​​ 服务。

接受 CORS 标头

要指定 ​​REST​​​ 服务接受 ​​CORS​​ 标头:

  1. 修改规范类以包含​​HandleCorsRequest​​ 参数。

要为所有调用启用 ​​CORS​​​ 标头处理,请将 ​​HandleCorsRequest​​​ 参数指定为 ​​1​​:

Parameter HandleCorsRequest = 1;

或者,要为某些调用启用 ​​CORS​​​ 标头处理,但不是调用,请将 ​​HandleCorsRequest​​​ 参数指定为​​“”​​(空字符串):

Parameter HandleCorsRequest = "";
  1. 如果将​​HandleCorsRequest​​​ 参数指定为​​“”​​​,请编辑规范类中的​​OpenAPI XData​​​ 块以指示哪些调用支持​​CORS​​。具体来说,对于操作对象,添加以下属性名称和值:
"x-ISC_CORS":true

例如,​​OpenAPI XData​​ 块可能包含以下内容:

"post":{
"description":"Creates a new pet in the store. Duplicates are allowed",
"operationId":"addPet",
"produces":[
"application/json"
],
...

添加 ​​x-ISC_CORS​​ 属性,如下所示:

"post":{
"description":"Creates a new pet in the store. Duplicates are allowed",
"operationId":"addPet",
"x-ISC_CORS":true,
"produces":[
"application/json"
],
...
  1. 编译规范类。此操作重新生成调度类,导致行为的实际变化。没有必要详细了解​​dispatch​​ 类,但请注意以下变化:
  • 它现在包含​​HandleCorsRequest​​ 参数的值。
  • ​URLMap XData​​​ 块现在包含对应于修改的操作的​​<Route>​​​ 元素的​​Cors="true"​​。

如果 ​​HandleCorsRequest​​​ 参数为 ​​0​​​(默认值),则对所有调用禁用 ​​CORS​​​ 标头处理。在这种情况下,如果 ​​REST​​​ 服务接收到带有 ​​CORS​​ 标头的请求,则服务会拒绝该请求。

重要提示:​​IRIS REST​​​ 服务支持 ​​OPTIONS​​​ 请求(​​CORS​​​ 预检请求),该请求用于确定 ​​REST​​​ 服务是否支持 ​​CORS​​​。此请求始终未经身份验证发送,并由 ​​CSPSystem​​​ 用户执行。此用户应具有 ​​REST​​​ 服务使用的任何数据库的 ​​READ​​​ 权限;如果没有,服务将响应 ​​HTTP 404​​ 错误。

定义如何处理 CORS 标头

当启用 ​​REST​​​ 服务以接受 ​​CORS​​​ 标头时,默认情况下,该服务接受任何 ​​CORS​​​ 请求。 ​​REST​​​ 服务应检查 ​​CORS​​ 请求并决定是否继续。例如,可以提供一个允许列表,其中包含仅包含受信任脚本的域。为此,需要:

  • 创建​​%CSP.REST​​​ 的子类。在这个类中,实现第一小节中描述的​​OnHandleCorsRequest()​​ 方法。
  • 修改规范类并重新编译,重新生成调度类。

最终结果是调度类从自定义类而不是从 ​​%CSP.REST​​​ 继承,因此使用对 ​​OnHandleCorsRequest()​​​ 的定义,它覆盖了默认的 ​​CORS​​ 标头处理。

定义 OnHandleCorsRequest()

在 ​​%CSP.REST​​​ 的子类中,定义 ​​OnHandleCorsRequest()​​​ 方法,该方法需要检查 ​​CORS​​ 请求并适当地设置响应标头。

要定义此方法,必须熟悉 ​​CORS​​ 协议的细节(此处不讨论)。

还需要知道如何检查请求并设置响应标头。为此,检查默认使用的方法是有用的,即 ​​%CSP.REST​​​ 的 ​​HandleDefaultCorsRequest()​​​ 方法。本节说明此方法如何处理源、凭据、标头和请求方法并提出变体建议。可以使用此信息来编写 ​​OnHandleCorsRequest()​​ 方法。

以下代码获取源并使用它来设置响应标头。一种可能的变体是根据允许列表测试来源。然后域被允许,设置响应头。如果不是,请将响应标头设置为空字符串。

; Get the origin
Set tOrigin=$Get(%request.CgiEnvs("HTTP_ORIGIN"))

#; Allow requested origin
Do ..SetResponseHeaderIfEmpty("Access-Control-Allow-Origin",tOrigin)

以下几行指定应包含授权标头。

; Set allow credentials to be true
Do ..SetResponseHeaderIfEmpty("Access-Control-Allow-Credentials","true")

以下行从传入请求中获取标头和请求方法。代码应测试是否允许标头和请求方法。如果允许,请使用它们来设置响应标头。如果不是,请将响应标头设置为空字符串。

; Allow requested headers
Set tHeaders=$Get(%request.CgiEnvs("HTTP_ACCESS_CONTROL_REQUEST_HEADERS"))
Do ..SetResponseHeaderIfEmpty("Access-Control-Allow-Headers",tHeaders)

#; Allow requested method
Set tMethod=$Get(%request.CgiEnvs("HTTP_ACCESS_CONTROL_REQUEST_METHOD"))
Do ..SetResponseHeaderIfEmpty("Access-Control-Allow-Method",tMethod)

重要提示:默认 ​​CORS​​​ 标头处理不适用于处理机密数据的 ​​REST​​ 服务。

修改规范类

在定义 ​​%CSP.REST​​​ 的自定义子类(包括 ​​OnHandleCorsRequest()​​ 的实现)后,执行以下操作:

  1. 编辑规范类中的​​OpenAPI XData​​​ 块,使​​info​​​ 对象包含一个名为​​x-ISC_DispatchParent​​ 的新属性。此属性的值必须是自定义类的完全限定名称。

例如,假设 ​​OpenAPI XData​​ 块如下所示:

"swagger":"2.0",
"info":{
"version":"1.0.0",
"title":"Swagger Petstore",
"description":"A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification",
"termsOfService":"http://swagger.io/terms/",
"contact":{
"name":"Swagger API Team"
},
...

假设 ​​%CSP.REST​​​ 的自定义子类名为 ​​test.MyDispatchClass​​​。在这种情况下,将修改 ​​XData​​ 块,如下所示:

"swagger":"2.0",
"info":{
"version":"1.0.0",
"title":"Swagger Petstore",
"description":"A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification",
"termsOfService":"http://swagger.io/terms/",
"x-ISC_DispatchParent":"test.MyDispatchClass",
"contact":{
"name":"Swagger API Team"
},
...
  1. 编译规范类。此操作重新生成调度类。会注意到该类现在扩展了自定义调度超类。因此它将使用​​OnHandleCorsRequest()​​ 方法。