跨源资源共享(CORS ↗)是一种使用 HTTP 标头授予在某个源上运行的 Web 应用程序访问其他源中选定资源权限的机制。当 Web 应用程序请求与其自身源不同的资源时(包括域、协议或端口),它会执行跨源 HTTP 请求。
为了使 CORS 请求到达受 Access 保护的站点,请求必须包含有效的 CF-Authorization cookie。根据请求类型的不同,这可能需要额外的配置:
如果您向受 Access 保护的域发送简单 CORS 请求且尚未登录,该请求将返回 CORS error。您可以通过以下两种方式解决此错误:
- 选项 1 — 手动登录并刷新页面。
- 选项 2 — 创建自动发送身份验证令牌的 Cloudflare Worker。此方法仅在 CORS 交换中涉及的两个站点都处于 Access 保护之下时才有效。
- 在浏览器中访问目标域。您将看到 Access 登录页面。
- 登录目标域。这将生成
CF-Authorizationcookie。 - 刷新发起 CORS 请求的页面。刷新会使用新生成的 cookie 重新发送请求。
如果您向受 Access 保护的域发送预检跨源请求,OPTIONS 请求将返回 403 错误。无论您是否已登录该域,都会发生此错误。这是因为根据设计,浏览器绝不会在 OPTIONS 请求中包含 cookie。因此,Cloudflare 将阻止预检请求,从而导致 CORS 交换失败。
您可以通过以下三种方式解决此错误:
- 选项 1 — 将 OPTIONS 请求绕过到源站。
- 选项 2 — 配置 Cloudflare 响应 OPTIONS 请求。
- 选项 3 — 创建自动发送身份验证令牌的 Cloudflare Worker。此方法仅在 CORS 交换中涉及的两个站点都处于 Access 保护之下时才有效。
您可以配置 Cloudflare 直接将 OPTIONS 请求发送到您的源站服务器。若要针对 OPTIONS 请求绕过 Access:
- 在 Cloudflare 仪表板 ↗中,转到 Zero Trust > Access controls(访问控制)> Applications(应用程序)。
- 找到将接收 OPTIONS 请求的源站,然后选择 Configure(配置)。
- 转到 Advanced settings(高级设置)> Cross-Origin Resource Sharing (CORS) settings(跨源资源共享 (CORS) 设置)。
- 开启 Bypass options requests to origin(将 OPTIONS 请求绕过到源站)。这将移除此应用程序的所有现有 CORS 设置。
为 Access JWT 强制执行 CORS 仍然非常重要 —— 只有在您的源站服务器中已建立 CORS 强制执行时,才应使用此选项。
您可以配置 Cloudflare 代表您响应 OPTIONS 请求。OPTIONS 请求永远不会到达您的源站。预检交换解决后,浏览器将发送包含身份验证 cookie 的主请求(假设您已登录受 Access 保护的域)。
若要配置 Cloudflare 如何响应预检请求:
-
在 Cloudflare 仪表板 ↗中,转到 Zero Trust > Access controls(访问控制)> Applications(应用程序)。
-
找到将接收 OPTIONS 请求的源站,然后选择 Configure(配置)。
-
转到 Advanced settings(高级设置)> Cross-Origin Resource Sharing (CORS) settings(跨源资源共享 (CORS) 设置)。
-
配置这些 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-Origin、Access-Control-Allow-Credentials、Access-Control-Allow-Methods 和 Access-Control-Allow-Headers。
-
选择 Save(保存)。
-
(可选)您可以通过使用
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 Access 保护的站点:example.com 和 api.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.com和api.mysite.com域
按照这些说明生成一个新的 Access 服务令牌。将 Client ID 和 Client Secret 复制到安全的地方,因为您将在后面的步骤中使用它们。
-
在 Cloudflare 仪表板 ↗中,转到 Zero Trust > Access controls(访问控制)> Applications(应用程序)。
-
找到您的
api.mysite.com应用程序,然后选择 Configure(配置)。 -
选择 **Policies(策略)**选项卡。
-
添加以下策略:
操作 规则类型 选择器 Service Auth(服务身份验证) Include(包含) Service Token(服务令牌)
打开终端并运行以下命令:
npm create cloudflare@latest -- authentication-workeryarn create cloudflare authentication-workerpnpm 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-
在 Cloudflare 仪表板 ↗中,转到 Workers & Pages 页面。
Go to Workers & Pages ↗ -
选择您新创建的 Worker。
-
在 **Triggers(触发器)**选项卡中,转到 **Routes(路由)**并添加
example.com/api/*。Worker 放置在example.com的子路径上,以避免发起跨源请求。 -
在 **Settings(设置)**选项卡中,选择 Variables(变量)。
-
在 **Environment Variables(环境变量)**下,添加以下机密变量:
CF_ACCESS_CLIENT_ID=<服务令牌 Client ID>CF_ACCESS_CLIENT_SECRET=<服务令牌 Client Secret>
Client ID 和 Client Secret 是从您的服务令牌中复制的。
- 启用每个变量的 **Encrypt(加密)**选项,然后选择 Save(保存)。
修改您的 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 问题时,我们建议执行以下步骤:
- 捕获包含所描述问题的 HAR 文件,并同时记录 JS 控制台的日志输出。这是因为仅凭 HAR 文件无法完全看清跨源问题背后的原因。
- 确保应用程序在所有 fetch 或 XHR 请求中都设置了
credentials: 'same-origin'。 - 如果您在 script 标签上使用 cross-origin 设置 ↗,则必须将其设置为 “use-credentials”。