前端-跨域

551 阅读5分钟

TODO

  • 跨域方案
  • CORS模块
  • JSONP
  • CORS处理流程图
  • 示例代码

什么是跨域

浏览器为了安全起见,对于浏览器所发出的请求实施同源策略,拦截网页发起不同源请求的响应。

何为不同源?请求链接协议,域名,端口,任意一个不一样,都判定为不同源。浏览器这里的比较是与当前窗口的链接相比。

跨域是指在浏览器中通过XMLHTTPRequest/fetch API发起跟当前页面链接不同源的请求,视为跨域。

跨域解决方案

CORS (跨域资源共享)

CORS,全称Cross-Origin Resource Sharing [1] ,是一种允许当前域(domain)的资源(比如html/js/web service)被其他域(domain)的脚本请求访问的机制,通常由于同域安全策略(the same-origin security policy)浏览器会禁止这种跨域请求。

CORS是如何处理跨域请求?

针对简单请求

  • 浏览器在请求头部加上origin字段(值为当前域),然后下发请求
  • 服务器接到请求后,需要在响应头部中增加Access-Control-Allow-Origin
  • 浏览器接到请求后:
  • 如果origin不在Access-Control-Allow-Origin范围内,浏览器会拦截该响应。
  • 如果origin在Access-Control-Allow-Origin范围内,其他头部限制字段就会接着生效
  • Access-Control-Allow-Credentials值为true时,请求对象才能获取cookie信息,请求参数withCredentials需设置为true。Access-Control-Expose-Headers可以放开获取某些非基本头部参数的信息。(Access-Control-Allow-Credentials和Access-Control-Expose-Headers均由服务器返回)

针对非简单请求

  • 针对需要发起的请求,先发出预检请求(请求方法:OPTIONS;头部信息包含Origin,Access-Control-Request-Method,Access-Control-Request-Headers)
  • 服务器接到预检请求后,根据服务器配置,返回对应响应,主要在响应头部上,响应头部需包含Access-Control-Allow-Origin,Access-Control-Allow-Methods,Access-Control-Allow-Credentials,Access-Control-Allow-Headers,Access-Control-Max-Age),表示是否支持该域的请求。
  • 接着按简单请求处理

无论是简单请求还是非简单请求,浏览器都对服务器下发了请求(简单请求下发的是原始请求,非简单请求下发的是预检请求)。最终js代码能否拿到响应数据,是由服务器返回的头部信息来决定,若头部校验不通过,则直接拦截该请求。

满足下面两个条件即为简单请求,反之为非简单请求

  • 请求类型:GET、POST、HEAD
  • 不包含自定义头部,这三个除外(Accept、Accept-Language、Content-Type(application/x-www-form-urlencoded、multipart/form-data、text/plain)

涉及的头部

  • 请求头部
    • Origin: 当前域
    • Access-Control-Request-Method:列出CORS 请求用到哪个HTTP方法
    • Access-Control-Request-Headers:指定 CORS 请求将要加上什么请求头
  • 响应头部
    • Access-Control-Allow-Credentials:表示是否允许访问cookie
    • Access-Control-Expose-Headers:这个字段是给 XMLHttpRequest 对象赋能,让它可以访问其他扩展的头部
    • Access-Control-Allow-Origin:表示支持哪些域请求,*表示支持任意
    • Access-Control-Allow-Methods:表示支持哪些请求方法
    • Access-Control-Allow-Headers:列出支持CORS请求头部字段
    • Access-Control-Max-Age:预检请求的有效期,在此期间,不用发出另外一条预检请求

注意事项

  • IE低版本不支持
  • 需要服务器配置

JSONP

JSONP(JSON with Padding)实际上是借助了script标签不受同源策略限制的特性,同时script标签加载回来的javascript代码会立即被执行的原理实现的。需要客户端跟服务器一起配合才能使用。

实现原理

  • 在js中动态加入一个script标签,src指向对应的请求链接
  • 通过设置一个参数告诉服务器返回的js代码中应该回调的函数名
  • 服务器接收到请求后,通过参数知道返回的js代码调用的函数名
  • 服务器处理完请求后,根据对应的js调用的函数名以及处理结果生成对应的js代码,返回给浏览器
  • 浏览器接到返回的js代码后,就会立即执行,达到请求-处理的效果
  • 同时发起多个jsonp请求,遵循先返回先执行原则,通过宏任务方式执行

注意事项

  • script标签是get请求加载,所以这个方案也支持get请求
  • 这种方法,有比较大的安全风险,容易受攻击,应开启https搭配使用
  • 由于script标签执行时是全局环境,尽量保证每次回调的函数名称是不一样的,不然有可能会导致后面的回调函数覆盖前面的回调函数

img标签

浏览器对于img标签是不受同源策略限制的,所以通过img标签也能向服务器发起请求。由于是图片标签,只能发起请求,无法处理服务器返回的数据。

这种方案常用于前端监控数据上报。

反向代理

反向代理是指请求下发到同源的服务器上,服务器根据请求的头部/路径转发到另一个域的服务器。这里也需要目标服务器放开请求进入。

这种方案的好处在于,在浏览器端基本不涉及跨域问题,相当于把跨域问题挪到后端解决。

总结

  • 对于简单的get请求跨域,使用img标签,JSONP实现,兼容性最佳
  • 在不考虑低版本的IE浏览器的情况且需要支持多种类型请求时,使用CORS方案
  • 上面两种都无法满足时,使用反向代理

参考链接