credit:https://medium.com/@baphemot
译自: https://medium.com/@baphemot/understanding-cors-18ad6b478e2b
呃还行, 但不够
如果你经常跟 AJAX call 打交道, 那么你肯定遇到过下面这个错误
如果你看到这条消息, 意味着响应失败了, 但你还是能在 Console 里的 Network 标签下, 看到返回的数据
那么, 这里到底是怎么一回事呢?
跨源资源共享(CORS)
你所遇到的这种行为就是浏览器跨域的实现
考虑到安全问题, 在跨域标准化之前, 如果你想调用一个节点在不同域的 API, 是不存在的这种调用, 会因为 Same-Origin 政策被阻止
设计 CORS 这种机制是为了, 第一, 使你所发出的请求能代表你自身; 第二, 阻止那些流氓 JS 发出的请求; 第三, 这种机制会被激活, 无论何时你发送请求到:
1). 不同的域名(eg. 应用在 example.com 调用 api.com)
2). 不同的子域名(eg. 应用在 example.com 调用 api.com)
3). 不同的端口(eg. 应用在 example.com 调用 example.com:3001)
4). 不同的协议(eg. 应用在 example.com 调用 example.com)
通过这种机制, 我们能够阻止黑客的脚本攻击, 以防当你登陆, 比如银行网站, 的时候替换你的验证信息
如果你的浏览器尝试发起一个不简单的请求(比如: 一个请求包含了 cookies, 或者 Content-type 不是
- application / x - ww - form - urlencoded
- ,
- multipart / form - data
或者 text-plain )这时候会调用有一种叫做 预检 (preflight) 的机制, 然后会发送一个 options 请求到服务器如果服务器的响应, 没有携带特定的 headers, 随后的简单 get 或 post 请求还是会发送, 但是浏览器不会允许 JS 去访问的收到的数据
如果你明确想要添加 cookies, 自定义 headers 和其他一些 features, 那这个请求就不再是一个简单请求那么服务器就不会合理地响应, 请求也不会被发送
Access-Control-Allow-What?
CORS 使用很少的 HTTP 请求头(在请求和响应里都是), 但是有一点你必须明白, 而且有能力去在工作中应用:
Access-Control-Allow-Origin
这个请求头一般会被服务器端返回, 它的值代表了哪些域名你有权可以访问 它的值可以为:
* 允许访问任何域
一个安全验证过的域名(eg. example.com)
如果你请求客户端传一些用作验证的请求头, 比如 cookies, 那么你就不能将 Access-Control-Allow-Origin 的值设定为 * 必须是安全验证过的域名才可以!
Access-Control-Allow-Credentials
如果一个服务器支持通过 cookies 来验证, 那么必须要在响应里带上这个请求头
True 是其唯一有有效的值
Access-Control-Allow-Headers
一个逗号分隔的 list, 存放代表服务器愿意支持的请求头(eg. 比如
x - authentication - token
, 你需要将其包含在 ACA header 里, 返回给前面提到的 options 请求, 否则你的请求会被 blocked)
Access-Control-Expose-Headers
跟上面相似, 这个请求头包含一系列用户可用的 headers, 这些 headers 会出现在真实响应里, 而且客户端是可以使用的其他的所有 header 会被 blocked
Access-Control-Allow-Methods
这个比较简单, 存放所有服务端支持的 HTTP 请求类型(比如 get,post)
Origin
客户端请求头的一部分, 其值包含客户端 app 启动处的域名 出于安全考虑, 浏览器将允许你去重写这个值
如何消除 CORS 错误
你不得不承认 CORS 并不是一种错误它是一种预期的机制为了去保护用户, 你, 还有你发送请求的目标网站
有时候缺乏合理的请求头是客户端的一种错误的行为(eg. 缺少验证信息比如 API key)
这里我将给你一些方法去解决错误, 选择哪种方法, 这取决于你所应用的场景:
A - 我开发前端, 后端我认识, 听我的 ;)
嗯这当然是最好的情况, 你就可以去实现合理的 CORS 响应在你所请求的服务器端如果一个 API 正在使用 node 的 express 框架, 你只要用一下 cors 的包就行了如果你想使你的网站更加合理安全, 你可能要考虑使用一个白名单给
Access - Control - Allow - Origin
请求头
B - 我开发前端, 后端我不熟, 暂时需要一个临时的解决方案 :)
这是第二好的情况, 因为这就是 A 情况, 只不过有一些时间限制如果你想临时解决这个问题, 你可以让你的浏览器忽略 CORS 机制, 举个栗子, 使用 ACAO Chrome 插件, 或者在用 Chome 的时候跑一下下面的 flags:
chrome--disable - web - security--user - data - dir
重要: 请记住这条命令会应用于所有网站, 并且存在于整个浏览器会话中请小心使用
另一个路子就是, 你可以使用 devServer.proxy(假设你使用 webpack 去 serve 你的 app)或者使用一个 CORS-as-a-service 解决方案, 比如 cors-anywhere.herokuapp.com/
C - 我开发前端, 我想要调后端? 不存在的 :`(
好吧, 现在事情就变得复杂了首先, 你应该可能需要搞清, 为什么服务器端没有发送一个正确的请求头
可能它们不允许使用第三方的库的 app 去访问? 可能它们的 API 只给服务器端的应用使用, 而不是浏览器? 可能你在请求时没有发送用于验证的 token?
如果你仍然认为你有能够通过浏览器得到数据, 你应该去写一个自己的代理, 存在于浏览器应用和 API 之间, 就像我们在方案 B 中所做的一样
Adding a proxy in the middle
这个代理服务器, 不是必须和你的应用跑在相同的域上只要使得这个代理服务器, 在与客户端交流时支持 CORS 就可以在与 API 交流时不是必须要支持 CORS
你可以写一个自己的平台, 或者使用一个已有的解决方案, 比如
www.npmjs.com/package/cor
记住, 这种方法可能存在安全风险, 如果你想要支持验证的话
更多关于 CORS
如果你想学更多关于 CORS 的细节, 我推荐你去查看更加细节化的 MDN article.
来源: https://juejin.im/post/5a7359876fb9a0634a38e389