【计算机网络】跨域

62 阅读5分钟

同源策略

同源策略就是 从源a源b发送请求,如果源a和源b的协议域名端口 不同的话,请求能成功发出,但是响应会被浏览器拦截

跨域请求 本身不是问题。但是必须得到 服务器(资源提供方)的同意

跨域是 浏览器服务器之间的问题。 服务器和服务器之间不存在这个问题

解决跨域的方式

cors 跨域资源共享

CORS的核心思想是:跨域请求的安全性应该由被请求的资源方(服务器)来决定,而不是请求方。

CORS 的工作机制:两种请求类型

CORS将跨域请求分为两类:简单请求非简单请求。浏览器处理这两种请求的方式完全不同。

3.1 简单请求 (Simple Request)

如果一个请求同时满足以下所有条件,它就是一个“简单请求”:

  1. 请求方法是以下三者之一:

    • GET
    • POST
    • HEAD
  2. HTTP头部信息不超出以下几种字段:

    • Accept
    • Accept-Language
    • Content-Language
    • Content-Type(值仅限于 application/x-www-form-urlencoded、multipart/form-data、text/plain) 表示请求体的类型
    • DPR
    • Downlink
    • Save-Data
    • Viewport-Width
    • Width

PixPin_2025-11-09_13-26-37.png

  1. 请求中没有使用 ReadableStream 对象。

简单请求的交互流程:

  1. 浏览器发送请求:  浏览器在发送请求时,会自动在HTTP头部添加一个 Origin 字段,表明请求来自哪个源

    GET /data HTTP/1.1
    Host: api.service.com
    Origin: http://my-app.com
    
  2. 服务器处理并响应:  服务器收到请求后,检查 Origin 头部。如果服务器允许这个源的请求,它会在响应头中加入 Access-Control-Allow-Origin

    HTTP/1.1 200 OK
    Access-Control-Allow-Origin: http://my-app.com
    Content-Type: application/json
    
    {"message": "Hello from API!"}
    

    如果服务器使用通配符 *,则表示允许任何源的请求

    Access-Control-Allow-Origin: *
    
  3. 浏览器处理响应:  浏览器收到响应后,检查 Access-Control-Allow-Origin 头部。

    • 如果该头部的值包含了当前源(http://my-app.com)或者为 *,浏览器就认为请求成功,将响应数据交给JavaScript。
    • 如果没有这个头部,或者头部的值不匹配,浏览器就会拦截响应,并在控制台抛出CORS错误
3.2 非简单请求 (Preflighted Request)

不满足“简单请求”条件的请求,就是“非简单请求”。例如:

  • 使用了 PUT, DELETE, PATCH 等方法。
  • Content-Type 为 application/json。
  • 请求头中包含了自定义头部,如 X-Token。

为什么需要“预检”?

这些“非简单”请求可能会对服务器数据产生 副作用(比如修改或删除数据)。在没有CORS的年代,服务器可能根本没想过会收到来自其他源的 DELETE 请求。如果浏览器直接发送,可能会造成意想不到的后果。

因此,为了安全,浏览器在发送真正的请求之前,会先发送一个“投石问路”的预检请求 (Preflight Request)

预检请求的交互流程(两步走):

第一步:预检请求 (Preflight Request)

  1. 浏览器发送OPTIONS请求:  浏览器使用 OPTIONS 方法,向服务器发送一个预检请求。这个请求不包含请求体,但包含了几个关键的头部:

    • Origin: 表明请求来源。
    • Access-Control-Request-Method: 告知服务器,实际请求将使用哪种HTTP方法。
    • Access-Control-Request-Headers: 告知服务器,实际请求将携带哪些自定义头部。
    OPTIONS /users/123 HTTP/1.1
    Host: api.service.com
    Origin: http://my-app.com
    Access-Control-Request-Method: PUT
    Access-Control-Request-Headers: X-Token, Content-Type
    
  2. 服务器响应预检请求:  服务器收到预检请求后,根据这些信息判断是否同意即将到来的实际请求。如果同意,它会在响应中返回一系列 Access-Control-* 头部,作为授权。

    HTTP/1.1 204 No Content
    Access-Control-Allow-Origin: http://my-app.com
    Access-Control-Allow-Methods: GET, POST, PUT, DELETE
    Access-Control-Allow-Headers: X-Token, Content-Type
    Access-Control-Max-Age: 86400
    
    • Access-Control-Allow-Methods: 允许的请求方法。
    • Access-Control-Allow-Headers: 允许的请求头部。
    • Access-Control-Max-Age: 预检请求的有效期(秒)。在此期间,浏览器无需为同样的请求再次发送预检。

第二步:实际请求 (Actual Request)

  1. 浏览器检查预检响应:  浏览器收到预检响应后,检查授权信息是否与即将发送的实际请求匹配。

    • 如果匹配(允许的源、方法、头部都OK),浏览器就会发送 真正的请求。这个请求的过程就和“简单请求”一样了。
    • 如果不匹配,浏览器会像处理简单请求失败一样,在控制台抛出CORS错误,并且不会发送实际请求
  2. 服务器处理实际请求并响应:  流程同简单请求。服务器响应时,仍然需要包含 Access-Control-Allow-Origin 头部。

4. CORS 核心:HTTP 头部详解

请求头部 (Request Headers)
  • Origin: (所有跨域请求都会带) 表明请求的来源。
  • Access-Control-Request-Method: (仅预检请求) 告知服务器实际请求的方法。
  • Access-Control-Request-Headers: (仅预检请求) 告知服务器实际请求携带的自定义头部。
响应头部 (Response Headers)
  • Access-Control-Allow-Origin:  (必需)  允许访问的源。可以是单个源,也可以是 *。
  • Access-Control-Allow-Methods:  (预检响应必需)  允许的方法列表,如 GET, POST, DELETE。
  • Access-Control-Allow-Headers:  (预检响应必需)  允许的头部列表。
  • Access-Control-Allow-Credentials: (可选) 是否允许发送Cookie等凭证。值为 true 或 false。
  • Access-Control-Expose-Headers: (可选) 默认情况下,浏览器JS只能获取到Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma这几个响应头。如果服务器想让客户端能访问其他头部(如自定义的 X-Pagination-Count),就必须在这里指定。
  • Access-Control-Max-Age: (可选, 仅预检响应) 预检请求的缓存时间(秒)。

前端设置代理服务器 解决跨域

前端服务器 不仅提供前端页面。还提供api代理服务。 因为跨域是浏览器和服务器之间的问题。服务器和服务器之间没有这个问题。