前端跨域问题-2024-12-20

2. 如何确认服务端是否开启跨域(CORS)

要确认服务端是否开启了跨域资源共享(CORS),可以通过以下几种方法进行检查:

  1. 浏览器开发者工具
    步骤:
  • 打开浏览器的开发者工具(通常按 F12 或右键点击页面选择“检查”)。
    切换到“网络”(Network)标签。
    发起一个跨域请求(例如通过页面上的某个按钮或直接在控制台使用 fetch 或 XMLHttpRequest)。
    查看请求的响应头。
    检查内容:

  • 查找响应头中是否有以下 CORS 相关头:
    Access-Control-Allow-Origin: 指定允许访问的源。如果值为 *,表示允许所有源;如果是具体的域名,则只允许该域名。
    Access-Control-Allow-Methods: 指定允许的 HTTP 方法(如 GET, POST, OPTIONS 等)。
    Access-Control-Allow-Headers: 指定允许的自定义请求头。
    Access-Control-Allow-Credentials: 如果设置为 true,表示允许发送凭证(如 Cookie)。

  1. 总结
  • 主要方法:通过浏览器开发者工具、命令行工具或 REST 客户端测试响应头,或者查看服务器配置文件。
  • 关键点:查找 Access-Control-Allow-Origin 和其他 CORS 相关响应头。
    通过这些方法,你可以有效地确认服务端是否开启了跨域资源共享(CORS)。

3跨域请求发起步骤

跨域请求在某些情况下会先通过 OPTIONS 方法发送一个预检请求(Preflight Request)。这是浏览器为了确保服务器允许该跨域请求而进行的安全检查。通过了预校验,才会发起post/get等请求。具体来说:

  1. 什么是预检请求?
  • 定义:预检请求是一个使用 OPTIONS 方法发送的 HTTP 请求,它用于询问服务器是否允许即将发送的实际请求。
  • 目的:确保服务器明确允许来自特定源的特定类型的请求(如特定的 HTTP 方法和自定义头)。
  1. 触发预检请求的条件
    预检请求会在以下情况之一发生时被触发:
  • HTTP 方法不是简单方法:即不是 GET、HEAD 或 POST。
  • 请求包含非简单头:例如 Authorization、Content-Type 不是 application/x-www-form-urlencoded、multipart/form-data 或 text/plain。
  • 请求体中有复杂内容类型:例如 application/json。
  1. 预检请求的工作流程
  2. 发送预检请求:
  • 浏览器自动发送一个 OPTIONS 请求到目标 URL。
  • 请求头中包含:
    • Access-Control-Request-Method: 即将发送的实际请求的方法。
    • Access-Control-Request-Headers: 即将发送的实际请求中包含的自定义头。
  1. 服务器响应预检请求:
  • 服务器必须响应 OPTIONS 请求,并在响应头中包含以下 CORS 相关头:
    • Access-Control-Allow-Origin: 允许的源。
    • Access-Control-Allow-Methods: 允许的 HTTP 方法。
    • Access-Control-Allow-Headers: 允许的自定义头。
    • Access-Control-Max-Age: 预检请求结果的有效期(可选),以秒为单位。
  1. 实际请求:
  • 如果预检请求成功,浏览器会发送实际请求。
  • 如果预检请求失败,浏览器会阻止实际请求并抛出错误。
  1. 示例
  • 发送预检请求
    http
OPTIONS /api/endpoint HTTP/1.1
Host: example.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: Content-Type, Authorization
Origin: https://client-domain.com
  • 服务器响应预检请求
    http
HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://client-domain.com
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Max-Age: 86400
  • 实际请求
    http
POST /api/endpoint HTTP/1.1
Host: example.com
Content-Type: application/json
Authorization: Bearer token
Origin: https://client-domain.com
  1. 避免预检请求
    如果你希望避免预检请求,可以确保:
  • 使用简单的 HTTP 方法(GET、HEAD 或 POST)。
  • 不使用自定义头或仅使用简单头(如 Content-Type: application/x-www-form-urlencoded、multipart/form-data 或 text/plain)。
  • 请求体内容类型为简单类型。
  1. 总结
  • 跨域请求:在某些情况下会先通过 OPTIONS 方法发送预检请求。
  • 触发条件:非简单方法、包含非简单头或复杂内容类型。
  • 解决方案:确保服务器正确处理预检请求并返回适当的 CORS 头。

通过理解这些机制,你可以更好地调试和配置跨域请求,确保它们按预期工作。

4. OPTIONS 预检请求是否会带上 Cookie

OPTIONS 预检请求(Preflight Request)默认情况下不会携带 Cookie。这是由浏览器的安全策略决定的,目的是确保预检请求本身不会触发任何副作用或安全风险。

  1. 预检请求的特点
  • 无凭证:预检请求不会发送凭证(如 Cookie、HTTP 认证信息等),即使你在实际请求中设置了 credentials: 'include'。
  • 自动发起:预检请求是由浏览器自动发起的,开发者无法直接控制其行为。
  • 目的:预检请求用于确认服务器是否允许即将发送的实际请求,而不是执行实际的操作。
  1. 为什么预检请求不带 Cookie
  • 安全性:预检请求的主要目的是验证服务器是否允许跨域请求。如果预检请求携带了 Cookie,可能会导致不必要的认证或状态变更,从而引发安全问题。
  • 标准化:根据 CORS 规范,预检请求不应包含凭证,以确保其作为纯粹的验证机制。
  1. 实际请求的行为
  • 携带凭证:只有在预检请求成功并通过后,实际请求才会根据设置携带凭证(如 Cookie)。如果你在 fetch 请求中设置了 credentials: 'include',并且服务器允许凭证传输(通过响应头 Access-Control-Allow-Credentials: true),那么实际请求会携带 Cookie。
  1. 示例代码
  • 发送带有凭证的实际请求
fetch('https://example.com/api/endpoint', {
  method: 'POST',
  headers: {
    "Content-Type": "application/json",
    [headerName]: token,
  },
  credentials: 'include', // 确保包含凭证
  body: JSON.stringify(data)
});
  • 预检请求(浏览器自动生成)
http
OPTIONS /api/endpoint HTTP/1.1
Host: example.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: Content-Type, Authorization
Origin: https://client-domain.com
  • 服务器响应预检请求
http
HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://client-domain.com
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Allow-Credentials: true
Access-Control-Max-Age: 86400
  • 实际请求(携带 Cookie)
http
POST /api/endpoint HTTP/1.1
Host: example.com
Content-Type: application/json
Authorization: Bearer token
Cookie: session=abc123
Origin: https://client-domain.com
  1. 总结
  • 预检请求:不会携带 Cookie 或其他凭证,以确保其作为纯粹的验证机制。
  • 实际请求:如果配置正确,会在预检请求成功后携带 Cookie。
  • 原因:这是为了确保预检请求的安全性和标准化行为。

通过理解这些机制,你可以更好地调试和配置跨域请求,确保它们按预期工作。

5. 302 重定向响应是否会返回 Access-Control-Allow-Origin

当一个请求返回 302 Found 状态码进行重定向时,Access-Control-Allow-Origin 头部字段的行为取决于几个因素,包括客户端和服务器的具体实现以及中间代理的行为。以下是详细的解释:

1. 标准行为

根据 CORS 规范302 重定向响应通常不会包含 Access-Control-Allow-Origin 头部字段。这是因为 302 是一个临时重定向响应,浏览器会自动跟随重定向到新的 URL,并且 Access-Control-Allow-Origin 头部字段应该出现在最终响应中,而不是重定向响应中。

2. 具体行为

  • 初始请求

    • 如果初始请求是一个跨域请求,并且服务器返回 302 重定向,浏览器会自动跟随重定向到新的 URL。
    • 浏览器会在重定向请求中再次检查 CORS 头部字段。
  • 重定向响应

    • 重定向响应本身通常不会包含 Access-Control-Allow-Origin 头部字段。
    • 浏览器会忽略重定向响应中的 CORS 相关头部字段。
  • 最终响应

    • 最终重定向到的目标 URL 的响应必须包含正确的 Access-Control-Allow-Origin 头部字段,以便浏览器允许跨域访问。
    • 如果最终响应缺少 Access-Control-Allow-Origin 头部字段,浏览器会阻止跨域请求,并抛出错误。

3. 特殊情况

  • 服务器配置

    • 某些服务器可能会在 302 重定向响应中包含 Access-Control-Allow-Origin 头部字段,但这并不是标准行为。
    • 这种做法可能导致浏览器忽略重定向响应中的 CORS 头部字段,仍然需要最终响应包含正确的 CORS 头部字段。
  • 中间代理

    • 中间代理(如负载均衡器或反向代理)可能会在重定向响应中添加或修改头部字段,但这同样不是标准行为。
    • 开发者应确保最终响应包含正确的 CORS 头部字段。

4. 示例

初始请求
GET /initial-endpoint HTTP/1.1
Host: example.com
Origin: https://client-domain.com
重定向响应 (302)
HTTP/1.1 302 Found
Location: https://target-domain.com/final-endpoint
最终响应
HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://client-domain.com
Content-Type: application/json

{"key": "value"}
  • 关键点
    • 重定向响应 (302) 不包含 Access-Control-Allow-Origin
    • 最终响应 (200) 必须包含 Access-Control-Allow-Origin 以允许跨域访问。

5. 注意事项

  • 确保最终响应包含 CORS 头部字段:无论是否有重定向,最终响应都必须包含 Access-Control-Allow-Origin 头部字段,以确保跨域请求的成功。
  • 避免在重定向响应中包含 CORS 头部字段:遵循标准行为,不在 302 重定向响应中包含 Access-Control-Allow-Origin

总结

  • 302 重定向响应:通常不会包含 Access-Control-Allow-Origin 头部字段。
  • 最终响应:必须包含 Access-Control-Allow-Origin 头部字段,以允许跨域访问。
  • 标准行为:浏览器会自动跟随重定向,并在最终响应中检查 CORS 头部字段。

确保你的服务器配置正确,最终响应包含适当的 CORS 头部字段,以避免跨域请求失败。

6. 后端接口处理 POST 接口跨域时是否需要处理 OPTIONS 方法

在处理跨域请求时,特别是涉及 POST 请求时,了解如何正确处理 OPTIONS 预检请求是非常重要的。以下是详细说明:

1. 什么是 CORS 和预检请求?

  • CORS (Cross-Origin Resource Sharing):是一种安全机制,用于限制从一个源加载的文档或脚本如何与来自另一个源的资源进行交互。
  • 预检请求 (Preflight Request):对于某些类型的跨域请求(如带有自定义头部字段的请求或 POST 请求,其中 Content-Type 不是 application/x-www-form-urlencodedmultipart/form-datatext/plain),浏览器会先发送一个 OPTIONS 请求,以确定实际请求是否安全可执行。

2. 为什么需要处理 OPTIONS 请求?

  • 验证请求:浏览器通过 OPTIONS 请求询问服务器是否允许实际的跨域请求。
  • 安全性:确保服务器明确同意跨域请求的条件(如允许的方法、头部字段等)。

3. 哪些情况需要 OPTIONS 预检请求?

  • HTTP 方法PUT, DELETE, PATCH, OPTIONS 等。
  • 自定义头部字段:任何不在简单请求头部字段范围内的头部字段。
  • Content-Typeapplication/json(不属于简单请求的 Content-Type)。

4. POST 请求的情况

  • 简单 POST 请求

    • 如果 POST 请求的 Content-Typeapplication/x-www-form-urlencodedmultipart/form-datatext/plain,并且没有自定义头部字段,则不需要预检请求。
    • 示例:
      POST /api/resource HTTP/1.1
      Host: example.com
      Origin: https://client-domain.com
      Content-Type: application/x-www-form-urlencoded
      
      key=value
      
  • 复杂 POST 请求

    • 如果 POST 请求的 Content-Typeapplication/json,或者包含自定义头部字段,则需要预检请求。
    • 示例:
      POST /api/resource HTTP/1.1
      Host: example.com
      Origin: https://client-domain.com
      Content-Type: application/json
      X-Custom-Header: custom-value
      
      {"key": "value"}
      

5. 处理 OPTIONS 请求

为了支持复杂的跨域请求,你需要在服务器上处理 OPTIONS 请求,并返回适当的 CORS 头部字段。以下是一个示例:

示例:处理 OPTIONS 请求
// 假设使用 Express.js 处理请求

app.options('/api/resource', (req, res) => {
  res.header('Access-Control-Allow-Origin', 'https://client-domain.com');
  res.header('Access-Control-Allow-Methods', 'POST, OPTIONS');
  res.header('Access-Control-Allow-Headers', 'Content-Type, X-Custom-Header');
  res.status(204).send(); // No content response
});

app.post('/api/resource', (req, res) => {
  res.header('Access-Control-Allow-Origin', 'https://client-domain.com');
  // 处理 POST 请求逻辑
  res.json({ message: 'Success' });
});
关键点:
  • Access-Control-Allow-Origin:指定允许的源。
  • Access-Control-Allow-Methods:指定允许的 HTTP 方法。
  • Access-Control-Allow-Headers:指定允许的自定义头部字段。
  • 状态码 204 No Content:表示成功处理预检请求,但不返回任何内容。
image.png

image.png
  • options 接口返回Access-Control-Allow-xxx 才会发起post请求。

6. 总结

  • 简单 POST 请求:不需要处理 OPTIONS 请求。
  • 复杂 POST 请求:需要处理 OPTIONS 请求,以确保浏览器允许实际的跨域请求。
  • 最佳实践:始终确保服务器正确处理 OPTIONS 预检请求,以支持复杂的跨域操作。

具体到你的代码

在你的 createEuAgent 函数中,使用的是 POST 请求,并且 Content-Type 默认为 multipart/form-data(通过 FormData 对象创建)。这种情况下,通常不需要预检请求,但如果存在自定义头部字段或其他复杂情况,仍需处理 OPTIONS 请求。

示例:处理 OPTIONS 请求

假设你的 createEuAgent 函数需要处理 OPTIONS 请求:

import express, { Application, Request, Response } from 'express';
import { baseRequest } from "@/service";
import { ERiskType } from "../riskTort/enum";

const app: Application = express();

// 处理 OPTIONS 请求
app.options('/digital/agent/addEuAgent.json', (req: Request, res: Response) => {
  res.header('Access-Control-Allow-Origin', '*'); // 或指定具体的源
  res.header('Access-Control-Allow-Methods', 'POST, OPTIONS');
  res.header('Access-Control-Allow-Headers', 'Content-Type, X-Custom-Header');
  res.status(204).send(); // No content response
});

// 处理 POST 请求
app.post('/digital/agent/addEuAgent.json', async (req: Request, res: Response) => {
  try {
    const { headerName, token } = (await getToken())?.data || {};
    
    // 创建 FormData 对象并添加参数
    const formData = new FormData();
    for (const key in req.body) {
      if (req.body.hasOwnProperty(key)) {
        formData.append(key, req.body[key]);
      }
    }

    const response = await baseRequest("/digital/agent/addEuAgent.json", formData, {
      method: "POST",
      headers: {
        [headerName]: token,
      },
    });

    res.header('Access-Control-Allow-Origin', '*'); // 或指定具体的源
    res.json(response);
  } catch (error) {
    res.status(500).json({ error: 'Internal Server Error' });
  }
});

// 其他路由和中间件...

app.listen(3000, () => {
  console.log('Server is running on port 3000');
});

关键点:

  • OPTIONS 处理:确保服务器能够处理 OPTIONS 请求并返回正确的 CORS 头部字段。
  • POST 处理:确保实际的 POST 请求也包含正确的 CORS 头部字段。

通过以上步骤,你可以确保后端接口正确处理跨域请求,包括 POST 请求及其相应的 OPTIONS 预检请求。

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容