跳转到内容
搜索文档

CORS

最后更新 查看 MarkdownAgent 设置

跨源资源共享(CORS)是一种使用 HTTP 标头授予在某个源上运行的 Web 应用程序访问其他源中选定资源权限的机制。当 Web 应用程序请求与其自身源不同的资源时(包括域、协议或端口),它会执行跨源 HTTP 请求。

为了使 CORS 请求到达受 Access 保护的站点,请求必须包含有效的 CF-Authorization cookie。根据请求类型的不同,这可能需要额外的配置:

允许简单请求

如果您向受 Access 保护的域发送简单 CORS 请求且尚未登录,该请求将返回 CORS error。您可以通过以下两种方式解决此错误:

手动进行身份验证

  1. 在浏览器中访问目标域。您将看到 Access 登录页面。
  2. 登录目标域。这将生成 CF-Authorization cookie。
  3. 刷新发起 CORS 请求的页面。刷新会使用新生成的 cookie 重新发送请求。

允许预检请求

如果您向受 Access 保护的域发送预检跨源请求,OPTIONS 请求将返回 403 错误。无论您是否已登录该域,都会发生此错误。这是因为根据设计,浏览器绝不会在 OPTIONS 请求中包含 cookie。因此,Cloudflare 将阻止预检请求,从而导致 CORS 交换失败。

您可以通过以下三种方式解决此错误:

将 OPTIONS 请求绕过到源站

您可以配置 Cloudflare 直接将 OPTIONS 请求发送到您的源站服务器。若要针对 OPTIONS 请求绕过 Access:

  1. Cloudflare 仪表板中,转到 Zero Trust > Access controls(访问控制)> Applications(应用程序)
  2. 找到将接收 OPTIONS 请求的源站,然后选择 Configure(配置)
  3. 转到 Advanced settings(高级设置)> Cross-Origin Resource Sharing (CORS) settings(跨源资源共享 (CORS) 设置)
  4. 开启 Bypass options requests to origin(将 OPTIONS 请求绕过到源站)。这将移除此应用程序的所有现有 CORS 设置。

为 Access JWT 强制执行 CORS 仍然非常重要 —— 只有在您的源站服务器中已建立 CORS 强制执行时,才应使用此选项。

配置对预检请求的响应

您可以配置 Cloudflare 代表您响应 OPTIONS 请求。OPTIONS 请求永远不会到达您的源站。预检交换解决后,浏览器将发送包含身份验证 cookie 的主请求(假设您已登录受 Access 保护的域)。

若要配置 Cloudflare 如何响应预检请求:

  1. Cloudflare 仪表板中,转到 Zero Trust > Access controls(访问控制)> Applications(应用程序)

  2. 找到将接收 OPTIONS 请求的源站,然后选择 Configure(配置)

  3. 转到 Advanced settings(高级设置)> Cross-Origin Resource Sharing (CORS) settings(跨源资源共享 (CORS) 设置)

  4. 配置这些 CORS 设置,使其与您的源站发送的响应标头相匹配。

    例如,如果您已将 api.mysite.com 配置为返回以下标头:

    headers: {
      'Access-Control-Allow-Origin': 'https://example.com',
      'Access-Control-Allow-Credentials' : true,
      'Access-Control-Allow-Methods': 'GET, OPTIONS',
      'Access-Control-Allow-Headers': 'office',
      'Content-Type': 'application/json',
    }

    然后转到 Access 中的 api.mysite.com,配置 Access-Control-Allow-OriginAccess-Control-Allow-CredentialsAccess-Control-Allow-MethodsAccess-Control-Allow-HeadersCloudflare One 中的 CORS 设置配置示例

  5. 选择 Save(保存)

  6. (可选)您可以通过使用 curl 向源站发送 OPTIONS 请求来检查您的配置。例如,

    curl --head --request OPTIONS https://api.mysite.com \
    --header 'origin: https://example.com' \
    --header 'access-control-request-method: GET'

    应该返回类似于以下内容的响应:

    HTTP/2 200
    date: Tue, 24 May 2022 21:51:21 GMT
    vary: Origin, Access-Control-Request-Method, Access-Control-Request-Headers
    access-control-allow-origin: https://example.com
    access-control-allow-methods: GET
    access-control-allow-credentials: true
    expect-ct: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
    report-to: {"endpoints":[{"url":"https:\/\/a.nel.cloudflare.com\/report\/v3?s=A%2FbOOWJio%2B%2FjuJv5NC%2FE3%2Bo1zBl2UdjzJssw8gJLC4lE1lzIUPQKqJoLRTaVtFd21JK1d4g%2BnlEGNpx0mGtsR6jerNfr2H5mlQdO6u2RdOaJ6n%2F%2BS%2BF9%2Fa12UromVLcHsSA5Y%2Fj72tM%3D"}],"group":"cf-nel","max_age":604800}
    nel: {"success_fraction":0.01,"report_to":"cf-nel","max_age":604800}
    server: cloudflare
    cf-ray: 7109408e6b84efe4-EWR

使用 Cloudflare Worker 发送身份验证令牌

如果您有两个受 Cloudflare Access 保护的站点:example.comapi.mysite.com,它们之间发起的请求将受到 CORS 检查。登录 example.com 的用户将被签发针对 example.com 的 cookie。当用户的浏览器请求 api.mysite.com 时,Cloudflare Access 会寻找针对 api.mysite.com 的特定 cookie。如果用户尚未登录 api.mysite.com,则请求将失败。

为了避免必须登录两次,您可以创建一个自动将身份验证凭据发送到 api.mysite.com 的 Cloudflare Worker。

前提条件

  • Workers 账户
  • 已安装 wrangler
  • 受 Access 保护的 example.comapi.mysite.com

1. 生成服务令牌

按照这些说明生成一个新的 Access 服务令牌。将 Client IDClient Secret 复制到安全的地方,因为您将在后面的步骤中使用它们。

2. 添加 Service Auth 策略

  1. Cloudflare 仪表板中,转到 Zero Trust > Access controls(访问控制)> Applications(应用程序)

  2. 找到您的 api.mysite.com 应用程序,然后选择 Configure(配置)

  3. 选择 **Policies(策略)**选项卡。

  4. 添加以下策略:

    操作 规则类型 选择器
    Service Auth(服务身份验证) Include(包含) Service Token(服务令牌)

3. 创建新 Worker

打开终端并运行以下命令:

npm create cloudflare@latest -- authentication-worker

这将提示您安装 create-cloudflare 包,并引导您完成设置。

进行设置时,请选择以下选项:

  • 对于 What would you like to start with?,选择 Hello World example
  • 对于 Which template would you like to use?,选择 Worker only
  • 对于 Which language do you want to use?,选择 JavaScript
  • 对于 Do you want to use git for version control?,选择 Yes
  • 对于 Do you want to deploy your application?,选择 No(部署前我们还会做一些修改)。

转到您的项目目录。

cd authentication-worker

打开 /src/index.js,删除现有代码并粘贴以下示例:

// The hostname where your API lives
const originalAPIHostname = "api.mysite.com";

export default {
	async fetch(request, env) {
		// Change just the host. If the request comes in on example.com/api/name, the new URL is api.mysite.com/api/name
		const url = new URL(request.url);
		url.hostname = originalAPIHostname;

		// If your API is located on api.mysite.com/anyname (without "api/" in the path),
		// remove the "api/" part of example.com/api/name

		// url.pathname = url.pathname.substring(4)

		// Best practice is to always use the original request to construct the new request
		// to clone all the attributes. Applying the URL also requires a constructor
		// since once a Request has been constructed, its URL is immutable.
		const newRequest = new Request(url.toString(), request);

		newRequest.headers.set("cf-access-client-id", env.CF_ACCESS_CLIENT_ID);
		newRequest.headers.set("cf-access-client-secret", env.CF_ACCESS_CLIENT_SECRET);
		try {
			const response = await fetch(newRequest);

			// Copy over the response
			const modifiedResponse = new Response(response.body, response);

			// Delete the set-cookie from the response so it doesn't override existing cookies
			modifiedResponse.headers.delete("set-cookie");

			return modifiedResponse;
		} catch (e) {
			return new Response(JSON.stringify({ error: e.message }), {
				status: 500,
			});
		}
	},
};

然后,将 Worker 部署到您的 Cloudflare 账户:

npx wrangler deploy

4. 配置 Worker

  1. Cloudflare 仪表板中,转到 Workers & Pages 页面。

    Go to Workers & Pages ↗
  2. 选择您新创建的 Worker。

  3. 在 **Triggers(触发器)**选项卡中,转到 **Routes(路由)**并添加 example.com/api/*。Worker 放置在 example.com 的子路径上,以避免发起跨源请求。

  4. 在 **Settings(设置)**选项卡中,选择 Variables(变量)

  5. 在 **Environment Variables(环境变量)**下,添加以下机密变量

    • CF_ACCESS_CLIENT_ID = <服务令牌 Client ID>
    • CF_ACCESS_CLIENT_SECRET = <服务令牌 Client Secret>

Client ID 和 Client Secret 是从您的服务令牌中复制的。

  1. 启用每个变量的 **Encrypt(加密)**选项,然后选择 Save(保存)

5. 更新 HTTP 请求 URL

修改您的 example.com 应用程序,以将所有请求发送到 example.com/api/ 而不是 api.mysite.com

现在,HTTP 请求应该在两个不同的受 Access 保护的域之间无缝运行。当用户登录到 example.com 时,浏览器向 Worker 发起请求,而不是向 api.mysite.com 发起。Worker 将 Access 服务令牌添加到请求标头中,然后将请求转发到 api.mysite.com。由于服务令牌与 Service Auth 策略匹配,因此用户不再需要登录 api.mysite.com

故障排除

一般情况下,在排查 CORS 问题时,我们建议执行以下步骤:

  1. 捕获包含所描述问题的 HAR 文件,并同时记录 JS 控制台的日志输出。这是因为仅凭 HAR 文件无法完全看清跨源问题背后的原因。
  2. 确保应用程序在所有 fetch 或 XHR 请求中都设置了 credentials: 'same-origin'
  3. 如果您在 script 标签上使用 cross-origin 设置,则必须将其设置为 “use-credentials”。

这篇文档对您有帮助吗?