假如我们的网站主站为example.com
,但同时存在论坛bbs.example.com
等分站,主站和分站之间互相调用接口,就会产生跨域问题。
在浏览器中,只有当两个资源地址的协议、端口、主机全部相同时,才说这两个资源是同源的。跨域请求一个图片、一个脚本是没有问题的,但是跨域发起Ajax(Fetch)请求是不允许的,这是因为Ajax能够使用JavaScript代码读取跨域后的返回结果响应给当前网站,这可能被用于钓鱼网站。
解决跨域问题,主要有CORS响应头、jsonp(已过时)两种方案。
CORS响应头原理其实很简单,就是在跨域调用时,API接口响应头里增加了Access-Control-Allow-Origin
等控制字段,标注了该接口允许被调用的来源地址、请求方法等,浏览器识别到这个标识后,便允许该跨域调用。
具体流程如下:
OPTIONS
类型的preflight
请求preflight
响应内容中的CORS响应头,决定是否允许发起跨域请求下面例子是一个跨域preflight
请求,我们服务端的CORS响应头设置了允许的源地址、允许的HTTP方法等。
这里要尤其注意的一点是跨域响应头要在所有的跨域请求响应中存在,不只是preflight
请求!因此CORS响应头一般在服务端框架的过滤器(还有很多种叫法,比如拦截器、中间件、切面等)中实现。
补充:CORS响应头支持IE8+。
CORS响应头主要有如下6个可以控制的地方:
CORS响应头 | 说明 |
---|---|
Access-Control-Allow-Origin | 允许的跨域来源,支持通配符* |
Access-Control-Allow-Credentials | 请求是否允许携带Cookie |
Access-Control-Allow-Methods | 请求允许的方法(用于preflight 请求) |
Access-Control-Allow-Headers | 请求允许的请求头(用于preflight 请求) |
Access-Control-Expose-Headers | 可在响应中列出的响应头 |
Access-Control-Max-Age | preflight 请求的缓存时间 |
注意:Access-Control-Allow-Origin
谨慎使用*
(即允许全部来源的跨域请求),在测试环境中这样设置无可厚非,但生产环境中还是建议白名单方式指定站点。
大多数现代的后端框架都对跨域请求做了统一支持,比如SpringBoot中,我们可以直接使用@CrossOrigin
注解标注控制器或方法,允许单组或单个接口跨域请求,也可以统一配置,允许整个工程的接口跨域请求,这里就不多介绍了。
虽然给服务器设置CORS响应头非常简单,但是如果为了兼容IE考虑,还是需要用jsonp的形式实现跨域Ajax请求。jsonp的原理也比较简单,下面举一个例子。
假设服务器jsonp接口的请求URL为http://xxx/api?callback=showData
,我们发起Ajax请求的过程是这样的:
<script src="http://xxx/api?callback=showData"></script>
,浏览器就会按照这个地址以JavaScript代码形式加载内容showData(真正的json数据)
showData(result)
,result参数其实就是真正需要的json数据,这个函数就会恰好被服务端返回的showData(真正的json数据)
调用,至此我们就跨域得到了真正的json数据jsonp的缺点:
随着时代发展,如今IE浏览器已经正式淘汰,因此jsonp方式也已经逐渐消失了。