在 REST 服务中支持 CORS

2022-08-05 09:26:10 浏览数 (1)

概述

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

CORS 简介

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

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

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

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

  1. DomOne 中的网页中的脚本包含对 DomTwo 域中的IRIS REST 服务的 XMLHttpRequestXMLHttpRequest 具有 CORS 的自定义标头。
  2. 用户查看此网页并运行脚本。用户的浏览器检测到与包含网页的域不同的域的 XMLHttpRequest
  3. 用户的浏览器向 IRIS REST 服务发送一个特殊请求,该请求指示 XMLHttpRequestHTTP 请求方法和原始网页的域,在本示例中为 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

代码语言:javascript复制
Parameter HandleCorsRequest = 1;

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

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

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

代码语言:javascript复制
      "post":{
        "description":"Creates a new pet in the store.  Duplicates are allowed",
        "operationId":"addPet",
        "produces":[
          "application/json"
        ],
        ...

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

代码语言:javascript复制
      "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.RESTHandleDefaultCorsRequest() 方法。本节说明此方法如何处理源、凭据、标头和请求方法并提出变体建议。可以使用此信息来编写 OnHandleCorsRequest() 方法。

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

代码语言:javascript复制
    #; Get the origin
    Set tOrigin=$Get(%request.CgiEnvs("HTTP_ORIGIN"))

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

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

代码语言:javascript复制
    #; Set allow credentials to be true
    Do ..SetResponseHeaderIfEmpty("Access-Control-Allow-Credentials","true")

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

代码语言:javascript复制
    #; 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 块如下所示:

代码语言:javascript复制
  "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 块,如下所示:

代码语言:javascript复制
  "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() 方法。

0 人点赞