一、引子
在实际的业务开发中,在接口服务端已经支持CORS的情况下, 你的浏览器上可能还是会出现跨域的报错。
接口服务端已经支持CORS相关response-headers:
但是还是发现浏览器请求会报跨域的错:
这是为什么呢???
二、让我们复习一下CORS的知识点
1.CORS定义
CORS 是 "Cross-Origin Resource Sharing" 的缩写,中文翻译为 "跨域资源共享"。CORS 是一种浏览器机制,它允许受限资源(如字体、JavaScript)在一个域上被另一个域访问。它通过在 HTTP 头部中使用一组新的 HTTP 头部来实现,这些头部允许服务器声明哪些源站点有权限访问它们的资源。
2.CORS 通过以下 HTTP 头部来实现:
请求头部:
Origin: 指示请求的来源(协议、域名和端口)。
Access-Control-Request-Method: 在预检请求中使用,指示实际请求将使用的方法。
Access-Control-Request-Headers: 在预检请求中使用,指示实际请求将使用的自定义头部。
响应头部:
Access-Control-Allow-Origin: 指示哪些来源可以访问资源。可以是具体的域名,也可以是通配符 *。
Access-Control-Allow-Methods: 指示允许的 HTTP 方法(如 GET、POST、PUT、DELETE)。
Access-Control-Allow-Headers: 指示允许的自定义头部。
Access-Control-Allow-Credentials: 指示是否允许发送 Cookie。
Access-Control-Expose-Headers: 指示哪些头部可以在响应中暴露给浏览器。
Access-Control-Max-Age: 指示预检请求的结果可以缓存多长时间
Access-Control-Allow-Origin这个header参数大家都很熟悉,但是很多人会忘记或不知道其实还有Access-Control-Request-Method、Access-Control-Request-Headers这两个头。
那他们是做什么的呢?这就涉及到了CORS设计中两个很重要点概念:简单请求和非简单请求
三、 简单请求和options预检请求
在 HTTP/1.1 中,当使用某些方法(如 POST、PUT、PATCH)发送跨域请求时,如果请求包含自定义头部(如 Content-Type 设置为非默认值),浏览器会先发送一个预检请求(OPTIONS 请求)来检查服务器是否允许该实际请求。这是 CORS(跨域资源共享)规范的一部分,用于确保跨域请求的安全性。
预检请求的触发条件
1.使用了非简单方法(如 PUT、DELETE、PATCH)。
2.使用了非简单头部(如 Content-Type 设置为application/json等)。
3.请求包含自定义头部。
简单请求的条件
如果请求满足以下条件,则不会触发预检请求:
1.使用简单方法(GET、POST、HEAD)。
2.使用简单头部:
Accept
Accept-Language
Content-Language
Content-Type(值为 application/x-www-form-urlencoded、multipart/form-data 或 text/plain)
3.请求不包含自定义头部。
四、Content-Type与COSR对options预检策略的关系
设置 Content-Type 为 application/x-www-form-urlencoded 通常不会触发预检请求的原因是,浏览器将这种请求视为“简单请求”。根据 CORS(跨域资源共享)规范,简单请求不会触发预检请求(OPTIONS 请求)。
简单请求的条件
使用简单方法:GET、POST、HEAD。
使用简单头部:
Accept
Accept-Language
Content-Language
Content-Type(值为 application/x-www-form-urlencoded、multipart/form-data 或 text/plain)
请求不包含自定义头部。
为什么不设置 Content-Type 可能会触发预检请求
当你不显式设置 Content-Type 时,浏览器可能会根据请求体的内容自动推断并设置 Content-Type。如果浏览器自动设置的 Content-Type 不符合简单请求的条件(例如,设置为 application/json),则会触发预检请求。
五、结论
1.浏览器CORS的设计是为了保证后台接口服务点安全性,不仅仅只是对请求发起方页面所在域名进行了限制;还会限制跨域请求的请求方法、携带的自定义头部等进行限制。
2.对跨域请求的请求方法、携带的自定义头部等进行限是基于options的预先请求机制来实现的。
3.因为历史原因浏览器设计了简单请求概念,对跨域下满足简单请求的请求不用进行option的提前安全校验,因为简单请求的风险很低、在服务端的安全策略也很完善,浏览器对他们开了绿灯。
4.不满足简单请求的请求 必须要先走options预请求校验其安全性。