跨域

跨域是指一个资源请求与其不在同一个域(源)的资源,不在同一个域(源)是指两个域的协议、域名或端口不同。

同源策略

出于安全考虑,浏览器制定了同源策略, 限制了某些跨域请求。同源策略是跨域问题产生的根源。但是,同源策略并没有限制所有的跨域请求,比如浏览器不限制加载嵌在<script>标签中跨域的js文件。

跨域资源共享机制(CORS)

跨域资源共享(CORS)是浏览器提供的一种跨域协商机制,让前后端协商是否可以发出跨域请求。
CORS添加了若干Access-controll-request-xxx 的头,给客户端声明自己的源、要使用的头部、用使用的请求方法;添加了若干Access-Controll-Allow-xxx的头,给服务端声明自己支持跨域的源、头部和方法。
CORS将请求分为简单请求和复杂请求,对于复杂跨域请求,发送真正请求之前要通过预检机制和后端协商。

简单请求

一个简单请求要满足以下所有条件:

  • 只能使用GETHEADPOST请求方法

  • 不得手动设置以下头之外的头

    • Accept
    • Accept-Language
    • Content-Language
    • Content-Type
    • DPR
    • Downlink
    • Save-Data
    • Viewport-Width
    • Width
  • Content-Type只能是下面的一种

    • application/x-www-form-urlencoded
    • multipart/form-data
    • text/plain

    对于简单请求,后端只需要在返回体里设置相应的Access-controll-Allow-xxx就可以了

复杂请求和预检机制

除了上述简单请求外,其它请求都是复杂请求。对于复杂请求,浏览器会首先使用OPTION方法发送一个预验请求(Preflighted requests)到后端,后端决定是否允许发送该跨域请求,将决定结果返回前端。只有预检通过之后,真实的请求才会发送。流程如下:

跨域问题及CORS机制

(图片来自:https://www.html5rocks.com/en/tutorials/cors/#toc-adding-cors-support-to-the-server)

CORS跨域实现

使用CORS机制,需要分清楚是简单请求还是复杂请求,因为复杂跨域请求会触发预检机制。对于简单跨域请求,后端只需要在响应体里返回Access-Controll-Allow就可以了,但是对于复杂请求,则需要实现一个Option方法来返回Access-Controll-Allow,或者将请求调整为一个简单请求。预检整体交互过程如下图所示:

跨域问题及CORS机制

一个常见的例子是,在响应里设置了Access-Controll-Allow,然后使用Jquery发送一个跨域POST请求,你会发现没有问题。接着使用 angular resourcesave方法发送同样一个跨域请求,你就会发现报错了。这是为什么呢?因为jquery默认使用Content-Type:application/x-www-form-urlencoded,所以它发送的是一个简单请求,但是,因为angular resource 默认使用Content-Type:application/json,所以它发送的是一个复杂请求,这触发了浏览器的预检机制。这时我们手动设置Content-Type:application/x-www-form-urlencoded,或者后端实现一个Option方法,

需要注意的是,改变content-type可能会导致后端解析数据出错,例如content-type:application/x-www-form-urlencoded,参数是以键值对形式保存的,很多后端框架都会做自动解析操作,而content-type: text/plain,参数的形式就不确定了,只能以原始数据流的方式保存(放在PayLoad里面),需要自己解析。详情请看 AJAX POST请求中参数以form data和request payload形式在servlet中的获取方式

参考:Server-Side Access Control (CORS)

分类:

技术点:

相关文章: