同源策略和跨域解决方案

70 阅读5分钟

同源策略

相信大多数前端Coder都听说过同源策略,此处简单的回忆一下。

所谓同源,是指三个相同,即

  • 协议相同
  • 域名相同
  • 端口相同

对于两个网址,如果能同时满足这三个相同,则我们称之为同源。有一点值得注意的是,即使是协议相同,端口相同,如果一个是域名访问,一个是IP访问,那么他们依然是非同源的。

同源策略的目的,是为了保证用户信息的安全,防止恶意的网站窃取数据。

如果非同源,那么将会有以下几种情况的限制。

  1. Cookie、LocalStorage和IdnexDB无法读取

  2. 无法操作非同源的DOM

  3. Ajax请求的数据不能获得

跨域解决方案

由于同源策略,一般情况下,我们对非同源的资源请求都会受到限制,但是依然还是有解决方案的。

JSONP

JSONP作为客户端与服务端通信的一种常用方式,其特点是简单,对浏览器的兼容性也比较好。

其基本思想是,网页通过添加一个<script>元素,向服务器请求JSON数据,这种做法不受同源策略限制;服务器收到请求后,将数据作为一个指定名字的回调函数的参数传回来。

function loadJSON(url){
    let script = document.createElement("script");
    script.src = url;
    document.body.append(script);
}

window.onload = function(){
    loadJSON("http://www.localhost.com/res/userinfo?callback=getUserInfo")
}

function getUserInfo(date){
    console.log(date);
}

当添加<script>标签后,浏览器就会请求该URL,并自动运行该脚本,该脚本的内容是对已知函数的调用(就像下面这样),从而我们就可以取到我们的数据。

// JSONP请求获取到的脚本内容,我们需要的数据作为参数传入回调函数中
getUserInfo({name:"xiaohe",age:18})

CORS跨域资源共享

JSONP是一个不错的跨域解决方案,但是其仍然存在的一定的缺点,JSONP只能发送GET请求。

CORS跨域资源共享是一种标准的跨域解决方案,解决了Ajax请求只能同源使用的限制,并适用于所有的请求方式。

一般的,Ajax请求分为两种,一种是简单请求,一种是复杂请求。如果一个请求是简单请求,则其必须满足以下两个条件:

  1. 请求方式是HEAD,GET,POST的其中一种。
  2. http请求头信息不超过以下几个字段:
  • Accept
  • Accept-Language
  • Content-Language
  • Content-Type:只限于三个值application/x-www-form-urlencodedmultipart/form-datatext/plain

若不能同时满足以上两个条件,则为复杂请求。

我们知道,当我们发送ajax跨域请求的时候,浏览器一般会先发送一个预检请求来判断是否支持跨域。预检请求是一个OPTIONS请求,没有请求体,在该请求中,请求头会包含几个比较重要的字段:

  • Origin: 该字段表示发送跨域请求的源。
  • Access-Control-Request-Headers: 该字段表示跨域请求所需要发送的请求头字段。
  • Access-Control-Request-Method: 该字段表示发送跨域请求的请求方式。

如果服务器同意此次跨域请求,那么就会设置对应的响应头字段:

  • Access-Control-Allow-Origin: 可以设置为*,表示所有的源都可以跨域请求该资源;也可以设置为请求头的Origin字段值,表示接受该源的跨域请求。
  • Access-Control-Allow-Methods: 该字段值必须包含请求头的Access-Control-Request-Method字段值,否则无法跨域。
  • Access-Control-Allow-Headers: 该字段值必须包含请求头的Access-Control-Reques-Headers字段值,否则无法跨域。

同时,这个真正的请求的响应头,也必须设置Access-Control-Allow-Origin字段,否则跨域依然不成功。

预检OPTIONS请求,响应头中有Access-Control-Allow-Origin字段

真正的请求,响应头无Access-Control-Allow-Origin字段,跨域不成功

一点题外话,其实同源策略并不是限制了非同源的请求,而是限制获取数据。

从上面的图我们可以看到,服务器已经返回了数据,但是由于浏览器的限制,并不能获取到数据。

其实,当ajax发送跨域请求的时候,并不一定会先发送预检请求:如果是简单请求,则会直接发送请求;只有复杂请求才会先发送预检请求。

对于复杂请求,如果在预检请求的响应头设置了Access-Control-Max-Age字段,那么也不必每一次跨域请求前都发送一次预检请求。其值是一个数,单位为s。

该字段表示 preflight request (预检请求)的返回结果(即 Access-Control-Allow-Methods 和Access-Control-Allow-Headers 提供的信息) 可以被缓存多久。

如果在测试的时候发现你的Access-Control-Max-Age并没有生效,那么你可能勾选了禁用缓存,取消勾选即可。

跨域请求可以携带Cookie吗?当然也是可以的。但是我不准备现在就说,因为我想去睡觉了。。。😴😴😴

挖个坑,以后回来补。。。🤷‍♂️🤷‍♂️🤷‍♂️

总结

来自前端菜鸟的总结,欢迎评论、指正、交流。

  1. 同源主要指三个相同:协议相同,域名相同,端口相同;
  2. 非同源将会受到行为上的限制:
    • Cookie,LocalStorage和IndexDB的读取限制
    • Dom的操作限制
    • Ajax跨域请求限制
  3. 跨域请求的解决方案有JSONP和CORS(跨域资源共享)
  4. Ajax跨域请求分为简单请求复杂请求,简单请求并不会先发送预检请求;复杂请求可以通过设置预检请求的响应头Access-Control-Max-Age字段来缓存预检信息,不必每一次请求都发送预检请求。
  5. 允许跨域请求的接口,其响应头必须得设置Access-Control-Allow-Origin