CORS(cross-origin resource sharing,跨域资源共享)是目前针对Ajax跨域的最好方法,原理很简单,在后端返回的响应头中设定白名单(如 Access-Control-Allow-Origin ),符合条件的即可得到资源。对于CORS的实现,浏览器端是自动执行相关操作,无需用户进行额外的处理,所以实现的关键在于后端。
两类请求
浏览器将CORS请求只要分为两个大类:简单请求和复杂请求,浏览器会对这两类请求分别进行不同的操作。
符合以下两大条件的即为简单请求:
- 请求方法是三者之一
GETHEADPOST
HTTP头部信息不超过这几种字段
AcceptAccept-LanguageContent-LanguageDPRDownlinkSave-DataViewport-WidthWidthContent-Type:限于这三个值application/x-www-form-urlencoded、multipart/form-data、text/plain简单请求
如果浏览器发现发送的是简单请求,那么会自动在请求头中添加Origin字段,指明请求源。譬如说,http://localhost:8080向URLhttp://localhost:3000/api发出跨域Ajax请求,请求头会如下所示:
服务器接收到请求会正常返回,浏览器会检验相关的响应头如Access-Allow-Control-Origin,如果不符合就会抛出错误,被XMLHttpRequest的onerror捕获。注意在这个过程当中,如果服务端仅仅设定了CORS相关的响应头,而没有人工针对origin做出任意额外的操作,相关接口的数据还是会发送回来,只不过会被浏览器拦截,导致Ajax无法获取到相关的数据。复杂请求
不符合之前所说的两大条件的即为复杂请求,如请求类型为PUT、DELETE等,或者带有自定义的请求头如X-Custom-Request: hello。
对于此类请求,浏览器在发出正式请求前会先发出一个预检请求 (preflight),通过此请求来向服务器确认此域名、请求方法、请求头等信息在不在许可范围内,得到了肯定答复以后才会发出XMLHttpRequest请求。
以下http://localhost:8080由axios发出的请求为例:
通过抓包可以看到浏览器一共向服务器发出两个请求:instance = axios.create({baseURL: "http://localhost:3000",headers: { "X-Custom-Header": "Rust" },});instance.get("/api").then((response) => {console.log(response);});

从上图看出,第一个请求为预检请求,请求方法为OPTIONS。浏览器在请求时会带上Access-Control-Request-Headers和Access-Control-Request-Method分别指定请求的头部和方法,响应返回后,浏览器根据CORS相关响应头来检查Ajax的请求信息,完全符合才会发出正式请求,如果有一项不符合条件,则会抛出错误。此外,如果服务器响应头不包含任何CORS相关响应头,浏览器也会认为服务器拒绝请求,从而抛出错误。
此外,并不是每一次的复杂请求都会进行预检,服务端可以设定Access-Control-Max-Age指定浏览器预检请求的时间,在这段时间内进行复杂请求可以不进行预检。Cookie相关处理
默认情况下进行跨域请求浏览器端是不会携带cookie,服务端也不会设置浏览器的cookie。如果浏览器要发送cookie,则要同时满足两个条件:
浏览器的xhr请求设定
withCredentials属性为true,说明同意服务器设定cookie和发送cookie- 服务器则要设定
Access-Control-Allow-Credentials响应头为true,说明同意接受cookie
如果服务端想设定cookie,则只需浏览器设定 withCredentials 为true即可,自身响应头可以不进行设定
此外,要携带cookie, Access-Control-Allow-Origin 就不能设定为 * ,否则报错
CORS响应头总结
Access-Control-Allow-Origin:<origin> | *允许访问的urlAccess-Control-Expose-Headers:默认情况下xhr只能获取Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma响应头,如果要访问更多,则需要服务器进行设定Access-Control-Max-Age:单位为秒,设定一定时间范围内不用再进行预检请求Access-Control-Allow-Credentials:是否允许浏览器带上cookie等身份信息Access-Control-Allow-Methods:允许的请求方法Access-Control-Allow-Headers:在预检请求应用,指定允许带上的请求头CORS请求头总结
Origin:指定源站的域名Access-Control-Request-Method:向服务器说明请求的方法Access-Control-Request-Headers:向服务器说明请求的头部总结
浏览器将CORS请求分为两类,简单请求和复杂请求。
- 对于简单请求,浏览器正常发出请求,但是会根据服务器返回的CORS相关响应头来判断是否接收数据
- 对于复杂请求,浏览器会先发出预检请求,此请求为OPTIONS请求,通过响应头判断服务器是否允许相关域名、方法和请求头。预检通过则会接着发出正常请求,不通过则直接抛出错误。
- 浏览器要携带cookie,需要请求时设定
withCredentials属性为true,服务端也要通过设定Access-Control-Allow-Credentials为true来表示允许接收 - CORS功能强大,所有请求方法都能支持,能允许特定网站来请求数据,能自行设定是否携带cookie
