小知识,大挑战!本文正在参与“ 程序员必备小知识 ”创作活动
本文同时参与 「掘力星计划」 ,赢取创作大礼包,挑战创作激励金
Cors跨域原理
Cors全称Cross-Origin Resource Sharing ,是官方推荐的跨域解决方案。
Cors跨域的原理实际上是浏览器与后端服务器通过一些 HTTP 协议头来做一些约定和限制。
实现Cors通信的关键主要是在后端,只要后端服务器实现了Cors接口,就可以跨域通信。
未跨域前,本页面向其他域请求资源时,控制台会报错👇
XMLHttpRequest(xhr)不能请求http://127.0.0.1:8080/user/selectUser,请求资源的响应头中没有设置Access-Control-Allow-Origin,所以Origin:http://localhost:8000是不允许跨域请求的。
在后端响应跨域请求时,比对前端的请求头Origin和已经设置好的响应头Access-Control-Allow-Origin👇
后端设置
Access-Control-Allow-Origin就是指定了哪些域名可以跨域向后端请求资源,前端浏览器通过Response Headers中的Access-Control-Allow-Origin就可以知道能不能把数据吐出来。
如果Origin指定的域名在Access-Control-Allow-Origin许可范围内,后端就会给前端返回Access-Control-Allow-Origin信息,告诉你“跨域成功啦!”,后端可以正常向前端传递数据了✌。
以上就是允许一个简单的跨域请求的做法,只需要后端设置响应头Access-Control-Allow-Origin。
Access-Control-Allow-Origin:是后端给前端反馈跨域成功的响应头字段。它的值要么是前端请求时请求头信息里Origin字段的值,要么是一个*,表示接受任意域名的请求。
简单请求和非简单请求
浏览器将Cors请求分成两类:简单请求和非简单请求。
简单请求
只要同时满足以下两大条件,就属于简单请求。
- 请求方法是以下三种方法之一:
head,get,post- HTTP的头信息不超出以下几种字段:
- Accept
- Accept-Language
- Content-Language
- Last-Event-ID
- Content-Type(只限于 application/x-www-form-urlencoded、multipart/form-data、text/plain这三种情况)
对于简单请求,浏览器会直接发送Cors请求,具体说来(如果你看懂上面你已经会了😎),就是在Request Headers中加入origin字段,用来说明本次请求来自哪个源,然后后端比对前端发送过来的Origin与后端已经提前设置好的Access-Control-Allow-Origin,决定是否同意这次请求。
😫如果Origin指定的源不在Access-Control-Allow-Origin范围之内,后端就会返回一个正常的HTTP回应告诉你错啦,而且浏览器发现Response Headers信息里不包含Access-Control-Allow-Origin 字段信息。👇
有种特殊情况💣:尽管请求失败,但返回的状态码依然可能为200,表明浏览器请求是发出去了的,后端也会正确返回,但是我们拿不到response的内容。
🤗如果后端发现请求的Origin在指定的源范围内,则会返回响应的请求结果,在Response Headers中会返回后端设置的相关Cors头部字段,包括Access-Control-Allow-Origin字段信息👇

非简单请求
非简单请求,也叫 预检请求。凡是不同时满足上面两个条件,就属于非简单请求。
- 请求方法是以下方法之一:
put,delete,patch、connect、options、trace- Content-Type 的值不属于下列之一:
- application/x-www-form-urlencoded
- multipart/form-data
- text/plain
当发生符合非简单请求的条件时,浏览器会自动先发送一个options请求,并设置相应的请求头。如果发现后端支持该请求,则会将真正的请求发送到后端,反之,如果浏览器发现后端并不支持该请求,则会在控制台抛出错误,如下:
如果非简单请求发送成功:👇
蓝框标注的为预检请求发送的查询,在Request Headers内会加入Access-Control-Request-Headers字段和 Access-Control-Request-Method字段。后端支持预检请求后,才会发送真正的请求(如图中红框所示)。可以看到,非简单请求Response Headers除了返回Access-Control-Allow-Origin,还多返回了几个字段:
| 响应头字段 | 说明 |
|---|---|
Access-Control-Allow-Headers | 表明请求时携带的自定义请求首部字段 |
Access-Control-Allow-Methods | 表明后端所支持的请求方法 |
Access-Control-Max-Age | 表明Cors缓存时间,即本次请求的有效期,单位为秒。在此期间,不会发出另一条预检请求。 |
写在后面
Jsonp跨域 VS Cors跨域
| Jsonp跨域 | Cors跨域 |
|---|---|
| 对浏览器的支持较好 | 大部分浏览器都支持,浏览器IE10以下不支持Cors |
只能发送get请求获取资源 | 支持所有类型的HTTP请求(get、post、put、delete等) |
| 错误处理机制不完善,如果动态注入js脚本成功,就会执行回调函数,如果错误却没有任何提示信息 | 可以通过onerror事件监听错误,并且浏览器控制台可以看到详细的报错信息,利于排查 |
| 需要前端后端共同配合才能跨域成功 | 前端照常发送请求,主要是后端在忙 |
| 只会发一次请求 | Cors的非简单请求会发两次请求(预检请求+实际请求) |
| callback参数注入和资源访问授权设置存在安全问题 | 可以在资源访问授权方面(Access-Control-Allow-Origin)进行限制 |
| 被攻击后也只能读取资源 | 被攻击后不仅能读取资源,还能修改 |
总结与Cors跨域相关的协议头
| 请求头(Request Headers)字段 | 说明 |
|---|---|
Origin | 当前请求源,发送请求时会自动生成 |
Access-Control-Request-Method | 预检请求专属,将实际请求所使用的HTTP方法告诉后端 |
Access-Control-Request-Headers | 预检请求专属,将实际请求所携带的首部字段告诉后端 |
| 响应头(Response Headers)字段 | 说明 |
|---|---|
Access-Control-Allow-Origin | 后端给前端反馈跨域成功的响应头字段。它的值要么是前端请求时请求头信息里Origin字段的值,要么是一个*,表示接受任意域名的请求。 |
Access-Control-Allow-Headers | 表明请求时携带的自定义请求首部字段 |
Access-Control-Allow-Methods | 表明后端所支持的请求方法 |
Access-Control-Max-Age | 表明Cors缓存时间,即本次请求的有效期,单位为秒。在此期间,不会发出另一条预检请求。 |
Access-Control-Allow-Credentials | 表明是否允许发送cookie。Cors跨域请求时,浏览器默认不会发送cookie。如果需要跨域传递cookie,前端需要设置withCredentials:true,同时后端也必须设置Access-Control-Allow-Credentials为true。 |
Access-Control-Expose-Headers | Cors请求时,XMLHttpRequest对象的getResponseHeader()方法只能拿到6个基本字段:Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma。如果想拿到其他字段,就必须在Access-Control-Expose-Headers里面指定。 |
solo理解,以上如有不对的地方,欢迎指教~
参考资料: