Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

浅谈 CORS #7

Open
KarthusLorin opened this issue Jun 26, 2020 · 0 comments
Open

浅谈 CORS #7

KarthusLorin opened this issue Jun 26, 2020 · 0 comments

Comments

@KarthusLorin
Copy link
Owner

浅谈CORS

CORS全称“跨站资源共享”(Cross-Origin Resource Sharing),它允许浏览器克服浏览器同源策略向跨域服务器发出请求。

同源策略

概念

说到CORS,那么就不得不提浏览器同源策略,所谓“同源”,是指服务器URL的三个相同:

1.协议相同

2.域名相同

3.端口相同

举个栗子:比如一个URL是http://www.example.com:80/a.html,那么:

http://www.example.com:80/b.html	// 同源
https://www.example.com:80/a.html	// 非同源(协议不同)
http://www.example1.com:80/a.html	// 非同源(域名不同)
http://www.example.com:81/a.html	// 非同源(端口不同)

限制

如果非同源,那么三种行为将受到限制:

1.非同源页面无法跨域读取浏览器本地数据存储(Cookie、LocalStorage和IndexDB)

2.非同源页面无法跨域获取DOM

3.非同源页面无法跨域发送AJAX请求

目的

那么,为什么浏览器要使用同源策略?

同源策略的目的,是为了保证用户的信息安全,防止被不法分子窃取数据。而众所周知,Cookie包含大量的登录信息,如果一个网页可以跨域访问另一个网站的Cookie,那么不法分子可以通过使用跨域访问获取Cookie然后冒充用户,为所欲为。

由此可见,同源策略是极其有必要的。

突破同源策略

但是,很多时候,我们需要跨域发送AJAX请求,此时我们就需要突破同源策略不允许发送跨域AJAX的规定。随着技术的发展,有很多技术可以实现跨域发送AJAX请求,常见的有以下三种:

1.JSONP

2.Websocket

3.CORS

JSONP

JSONP是CORS技术出来之前最常用的跨域解决方案,最大的特定是兼容性好,简单,不需要进行大的服务器改动。它的基本思路是通过动态添加一个script标签,向服务器请求脚本,脚本中一般调用一个客户端定义的函数,将数据作为参数,调用客户端的函数,而客户端通过操作该函数,可以使用被当做参数传过来的数据。

因为服务器不限制script的跨域,所以不受跨域影响。

Websocket

众所周知,Websocket是一个持久化协议,常用于解决服务器推送问题。但是,实际上Websocket其实支持跨域通信。通过设置Websocket的origin的字段,可以规定允许跨域的站点。

上面两种方法虽然可以解决跨域,但是,都有着各种问题。

庆幸的是,本文的主角:CORS的出现,彻底解决了跨域问题。

CORS

浏览器将跨域AJAX请求分为两类:简单请求和非简单请求,对应有两种不同的处理方式。

简单请求

何为简单请求?

简单请求就是满足以下两个条件的请求:

1.请求方法为HEAD、GET和POST

2.HTTP请求头只包含:AcceptAccept-LanguageContent-LanguageLast-Event-ID以及值为application/x-www-form-urlencodedmultipart/form-datatext/plain三者之一的Content-Type

对于简单请求,浏览器可以直接发送请求到服务器,但是会在请求头中添加一个origin字段,该字段用来说明请求的来源。服务器会识别该字段,判断是否允许跨域。

如果允许跨域,服务器会返回结果并在响应头上添加三个字段:

1.Access-Control-Allow-Origin

该字段的值为Origin字段的值,或者是*,表示服务器接受任何源的跨域请求。

2.Access-Control-Allow-Credentials

可选字段,它表示是否允许发送Cookie,值为true时,表示发送请求的时候允许发送Cookie,如果不包含该字段,则表示不允许发送Cookie。

值得一提的是,如果服务器允许发送Cookie,那么不允许将Access-Control-Allow-Origin的值设为*

3.Access-Control-Expose-Headers

可选字段,在没有该字段的情况下,针对跨域请求,XHR对象的getResponseHeader()方法只能拿到Cache-ControlContent-LanguageContent-TypeExpireLast-ModifiedPragma这六个字段,该字段可以设置额外可以拿到的字段。

非简单请求

不满足简单请求的跨域请求都是非简单请求,比如PUT或DELETE方法。

不同于简单请求的直接向服务器请求,非简单请求会在发送之前,先进行一次“预检”(preflight),即,向服务器发出一个OPTIONS请求,查询服务器是否允许它进行跨域请求。

如果服务器不通过“预检”,会返回一个error,客户端可以通过onerror事件进行捕获。

当服务器通过“预检”后,服务器会进行响应,响应头中含有CORS的相关字段,分别是:

1.Access-Control-Allow-Origin

该字段和简单请求中的同名字段一样。

2.Access-Control-Allow-Methods

该字段表示服务器支持跨域的所有方法,是一个逗号分隔的字符串,如:POST,DELETE。

3.Access-Control-Allow-Headers

该字段表示服务器支持的所有头信息,也是一个逗号分隔的字符串。

4.Access-Control-Allow-Credentials

可选字段,与简单请求中的同名字段一样。

5.Access-Control-Max-Age

可选字段,在一段时间内,浏览器对同一个域名进行非简单跨域请求,只对第一次进行“预检”,而这一次“预检”的结果将被缓存,接下来的请求都通过该结果进行判断。该字段就是用来设置“预检”结果缓存的时间长短,可以将其值设为-1来禁用“预检”缓存。

接收到服务器通过“预检”的响应后,客户端会正式发送真正的请求,接下来的处理方式和简单请求一致。

总结

在当前开发中,当不需要兼容老式浏览器中,我们一般采用CORS的方式进行跨域请求,因为相比Websocket,CORS支持非长连接场景;相比JSONP,CORS支持所有HTTP请求,用法更加平滑。

当然,值得一提的是,当你需要兼容老式浏览器时,JSONP是你唯一的选择~

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant