Skip to content

CORS(跨域资源共享)

CORS(Cross Origin Resource Sharing) 即跨域资源共享。 浏览器使用同源策略(Same Origin Policy),使得不同源的网站不能互相访问。 CORS 不是一个安全漏洞,而是为了降低浏览器对资源共享的限制。

什么是同源?

如果两个 URL 的协议(protocol)、主机(host)和端口号(port)相同,则视为同源。

浏览器同源策略解析

定义后端接口

java
@RestController
@RequestMapping("/cors")
public class CorsController {

    private final Logger logger = LoggerFactory.getLogger(CorsController.class);

    @GetMapping("/get")
    public String get() {
        logger.info("handle get");
        return "OK";
    }
}

打开任意网站,执行如下 JS 代码

java
fetch("http://localhost:18080/cors/get")
    .then(res => res.text())
    .then(text => console.log(text))

浏览器会输出如下错误

text
Access to fetch at 'http://localhost:18080/cors/get' from origin 'https://juejin.cn' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

但是后端服务会正确执行,并输出 handle get

浏览器预检策略

现代浏览器在执行跨域请求前会判断是否需要预检,通过预检结果判断是否需要执行原始请求。

为什么需要预检

现代应用程序开发中一个请求可能进行很多复杂的操作, 浏览器在发起请求前进行一个预检(Preflight Request), 则可以避免跨域请求时服务器执行不必要的处理。

判断是否需要预检

浏览器判断是否需要预检标准是全球是否是复杂请求或者之前的预检请求是否还在有效期内。

复杂请求:

  • 除 GET、POST 和 HEAD 之外的 HTTP 请求。
  • 请求包含 Accept、Accept-Language 和 Content-Length 之外的请求头。
  • 请求头包含 Content-Type 但是值不是 application/x-www-form-unlencodedmultipart/form-datatext/plain 的请求。

什么是预检请求

预检请求是一个 OPTIONS 请求, 包含 Origin 请求头,还可能包含如下请求:

  • Accept-Control-Request-Method
  • Accept-Control-Request-Headers

使用如下JS代码进行请求测试

javascript
fetch("http://localhost:18080/cors/get", {
headers: {
"TEST": "TEST"
}
}).then(res => res.text()).then(text => console.log(text))

服务器需要对 OPTIONS 请求进行响应,并设置对应的请求头: Access-Control-Allow-Origin: 允许跨域访问的源站地址,跨域使用通配符 *,如果服务器设置了单一源值, 则需要设置 Vary响应头的值为 Origin(表示服务端对于不同的 Origin 值会返回不同的内容)。

txt
Access-Control-Allow-Origin: http://localhost:5173
Vary: Origin

Access-Control-Max-Age: 指定了预检请求的结果能够被多少秒。

txt
Access-Control-Max-Age: <delta-seconds>

Access-Control-Allow-Credentials: 指定了实际的请求是否可以使用 credentials, 对于非预检请求指定了当浏览器的 credentials 设置为 true 时是否允许浏览器读取 response 的内容。

txt
Access-Control-Allow-Credentials: true

Access-Control-Allow-Methods: 指定了访问资源时允许使用的请求方法。

txt
Access-Control-Allow-Methods: <method>[, <method>]*

Access-Control-Allow-Headers: 指明了实际请求中允许携带的标头字段。

txt
Access-Control-Allow-Headers: <header-name>[, <header-name>]*