CORS跨域资源共享

为什么会出现跨域的现象?

因为在现在大多数都是前后端分离开发的情况下,前端资源和接口域名不同,触发了浏览器的同源安全策略,当接口返回数据到浏览器端时,被浏览器拦截,也就是我们平时所说的跨域。 所以整个跨域产生的根源是浏览器

同源指的是,协议、域名、端口都相同的Url,同源策略属于浏览器的核心安全功能,能够很好的防御XSS、CSRF攻击。

什么是CORS?

CORS,全称是"跨域资源共享",它允许浏览器向跨域服务器发送XMLHttpRequest请求,从而克服了原先只能请求同域服务器的限制。整个跨域过程都是浏览器在参与,对于前端开发者来说,CORS请求和普通请求在代码上没有区别。

目前几乎所有浏览器都支持CORS。IE9以及以下版本不支持,需要采用其它跨域方案。

IE9以及以下版本跨域解决方案

  1. JSONP(JSON with Padding):

    • JSONP 是一种通过动态创建<script>元素进行跨域请求的方法。服务器返回的数据被包裹在回调函数中,该回调函数会在客户端执行。这种方法仅支持 GET 请求,且需要服务器支持返回 JSONP 格式的数据。
  2. 代理服务器:

    • 在同源策略下,通过在同一域中设置一个代理服务器,将跨域请求发送到代理服务器,再由代理服务器转发请求到目标服务器,并将响应返回给客户端。这样可以绕过浏览器的同源策略。
  3. XDomainRequest:

    • Internet Explorer 8 和 9 提供了 XDomainRequest 对象,可以用于发送跨域请求。但是需要注意,它仅支持一部分的 HTTP 功能,并且有一些限制。

CORS涉及到的配置项

  • Access-Control-Allow-Origin
  • Access-Control-Allow-Credentials
  • Access-Control-Request-Method
  • Access-Control-Allow-Methods
  • Access-Control-Request-Headers
  • Access-Control-Allow-Headers
  • Access-Control-Expose-Headers
  • Access-Control-Max-Age

CORS将请求分为简单请求和复杂请求

简单请求

触发条件

  • 请求方法是HEAD、GET、POST之一。
  • HTTP请求头信息不超出以下几种字段:
    • Accept
    • Accept-Language
    • Content-Language
    • Last-Event-ID
    • Content-Type:请求数据的类型。只限于三个值:application/x-www-form-urlencoded、multipart/form-data、text/plain
    • ....还有协议其它冷门header浏览器默认携带的

简单请求响应过程流程

1、浏览器发出CORS请求️并在头信息添加一个Origin字段,该Origin字段用来标识本次请求来自哪个源(协议、域名、端口)。

例如:目前我们的页面域名大多数是h5-cdn.58.com,接口是其它域名,所以我们的请求头都有一个Origin字段

2、服务器收到请求后,执行业务逻辑。根据请求头Origin判断是否允许跨域
  • 如果Origin在白名单内,那么服务器返回的响应将会多出几个信息头

Access-Control-Allow-Credentials: true: 用于在请求要求包含 credentials(Request.credentials 的值为 include)时,告知浏览器是否可以将对请求的响应暴露给前端 JavaScript 代码。

如果是CORS请求,前端加了Request.credentials,那么服务端必须添加Access-Control-Allow-Credentials: true

Access-Control-Allow-Origin: https://h5-cdn.58.com:CORS响应头中必须存在的字段。它的值要么是请求携带的Origin值,要么是*:表明接受任意域名请求

  • 如果Origin不在白名单内,那么服务端将返回正常的相应。
3、浏览器在收到服务端响应后,会判断Access-Control-Allow-Origin等是否符合条件,如果不符合,将会拦截响应,触发跨域

复杂请求

如果请求域名和页面域名相同,那么就不会存在复杂请求这一说法,简单请求就可以满足需求。

触发条件

  • 使用了PUT、DELETE等请求方法。
  • 使用了自定义请求头
  • Content-Type 为 application/json

Access-Control-Request-Headers【Request Headers】

用在预检请求中,浏览器使用该头部通知服务器真正的请求将要使用的自定义请求标头字段。

Access-Control-Allow-Headers【Response Headers】

用于预检请求中,列出了将会在正式请求的 Access-Control-Request-Headers 字段中出现的首部信息。

  • 不设置credentials
    • CORS请求,不携带任何Cookie。即便Cookie是同站点。
    • 非CORS请求(同域)场景下,会默认携带同站点的Cookie。
  • 设置credentials
    • CORS请求场景,携带同站点的Cookie。
    • 非CORS请求(同域)场景下,携带同站点的Cookie。所以该场景可以不设置credentials

同站:域名相同或者域名之间为子域名的关系


Access-Control-Request- Method 【Request Headers】

用于通知服务器在真正的请求中会采用哪种HTTP方法。因为预检请求所使用的方法总是OPTIONS,与实际请求所使用的方法不一样,所以这个请求头是必要的。

Access-Control-Allow-Methods 【Response Headers】

对预检请求的应答中明确了客户端所要访问的资源允许使用的方法或方法列表。

那么针对于简单请求或者同域请求时,服务端未设置Access-Control-Allow-Methods,我们能正常请求吗?

可以的。

简单请求:浏览器会默认允许的方法是 GET、POST 和 HEAD。同域请求:默认所有的请求方法都支持。


Access-Control-Expose-Headers 【Response Headers】

允许服务器指示那些响应标头可以暴露给浏览器中运行的脚本,以响应跨源请求。CORS默认情况下,JS只能访问到以下其中Response Headers:

如果是非CORS请求,那么默认是可以访问到所有的响应头的。


Access-Control-Max-Age 【Response Headers】

表示预检请求的返回结果(即 Access-Control-Allow-MethodsAccess-Control-Allow-Headers 提供的信息)可以被缓存多久。单位:秒。

为什么要发送预检请求?

预检请求出发的前提

  • 跨域请求
  • 复杂请求类型,使用了自定义header或者常规请求以外的类型。

基于以上两点前提,预检请求的存在

  • 能给确保浏览器只在服务器确认安全的情况下发送实际请求。这有助于防止恶意网站发起对用户敏感数据的恶意请求,可以避免跨域请求对服务器用户数据产生未预期的影响。
  • 不被允许的请求提前被拦截,能给减少一些无用的运算逻辑。

域名添加了页面域名为白名单,但是前端进行post请求时,还是会出现跨域,why?

  • 如果是简单请求,添加白名单就可以解决跨域问题。
  • 如果是复杂请求,需要后端同学在响应头内添加Access-Control-Allow-Headers、Access-Control-Allow-Methods字段
    • Access-Control-Allow-Methods的值:设置成真实请求的请求方法,值和Access-Control-Request-Method对应。
    • Access-Control-Allow-Headers的值:添加的自定义请求header,值和Access-Control-Request-Headers对应。

Post请求是不会导致请求变为复杂请求,但是我们平时使用的请求库Axios或者其它,当不传Content-Type时,会自动根据数据类型设置Content-Type。

如果数据是普通对象,请求的Content-Type会默认设置为application/json。 如果是URLSearchParams对象,那么会默认设置为application/x-www-form-urlencoded;charset=utf-8。

这也是平时我们没有设置Content-Type和自定义header,请求却变成了复杂请求的大多数原因。

相关推荐
To_OC10 小时前
别再串行写 await 了,Promise.all 才是并行请求的正确打开方式
前端·javascript·promise
vipbic10 小时前
中后台越做越乱后,我用插件化把它救回来了
前端·vue.js
Hyyy11 小时前
Computer Use 适合做什么,不适合做什么——一次真实使用后的思考
前端
小和尚同志11 小时前
前端 AI 单元测试思考与落地
前端·人工智能·aigc
invicinble12 小时前
c端系统,其实更像一个信息展示平台
前端
李姆斯13 小时前
管理是否可以被完全量化
前端·产品经理·团队管理
名字还没想好☜14 小时前
Next.js 中间件实战:鉴权、重定向与 A/B 分流
开发语言·前端·javascript·中间件·react·next.js
广州灵眸科技有限公司14 小时前
瑞芯微RV1126B开发板(EASY-EAI-PI2) INI文件操作
java·前端·javascript·网络·人工智能
kaiking_g14 小时前
vue项目中,静态导入 vs 动态导入 详解
前端·javascript·vue.js
江华森14 小时前
Web 开发基础:HTTP 与 REST
前端·网络协议·http