浏览器实现对不同源的网址的资源获取的行为.详见维基百科.这篇文章的起因来源于需要向别的部门进行资源请求, 不在同一个域, 资源请求不到, 等了两天还没解决后便通过node的express来代理请求, 解决了这个问题.
定义(WHAT)
- 什么叫不同源(满足下列条件任意一个)
- 不同的域名(domain)
baidu.com
和google.com
这样名称不相同的就叫不同 domain
- 不同的端口
localhost:8000
和localhost:3000
- 如果不写端口号, 根据协议有默认值
- http: 80
- https: 443
- ftp: 21
- 不同的协议
- http https ftp ws wss等两两都属于不同地方协议.
- 什么叫跨域请求?
利用ajax, image, script等方式, 向不同域请求资源的行为称为跨域请求, 浏览器会与服务端进行沟通, 去判断是否可以进行跨域资源获取, 从而决定请求时成功还是失败.
假设当前域为(可在请求头中查看)
Origin: http://www.foo.com
服务器端设置:
Access-Control-Allow-Origin: http://www.foo.com
如果时公共资源:设置为 *
, 即让任意域的请求都可以响应.
- 跨域驳回
如果服务器的设置与当前请求的Origin不匹配, 那么浏览器就会驳回这个请求, 无法请求到任何资源, 并会在console中报CORS的错误.所以请注意, 跨域请求并不是没有发请求, 只是浏览器发现跨域后拦截了响应.
为什么(WHY)
小网站可能把所有的资源都放在同一个域下, 但是大型网站内容太多了, 一个服务器或者一个域是放不下了, 而且跨部门的接口调用也很常见, 所以需要使用CORS. 那么又会扯出一个新的问题, 为什么要有同源策略?一句话: 你会同意www.porn.com
这个网站访问到你爱存不存的银行账户吗?
这里需要区别一下CSRF(Cross-Site-Request-Forgery)
- CSRF不需要响应, 只需要你的身份凭证向第三方发送请求.
大致流程:- 登陆支付宝账户
- 访问a.com
- a.com向支付宝发送转账到hacker的请求
- 浏览器发现之前支付宝的cookie信息还未过期, 带着cookie一起发送过去
- 支付宝发现请求中带有之前已验证的cookie, 认为是本人操作, 同意转账
可以看到CSRF更多的是在服务端的防御性. 而CORS是着眼客户端的资源获取.他们都属于同源策略下的产物, 但有很大的区别
怎么做(HOW)
实现跨域的具体方式:
最常用的: XMLHTTPRequest
const xhr = new XMLHTTPRequest()
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
// 判断响应有效性
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) {
console.log(xhr.responseText)
} else {
console.error(‘something error: ‘, xhr.status)
}
}
}
xhr.open(‘get‘, ‘www.foo.com‘, true);
xhr.send(null)
open方法参数
- 两个必选参数:
- method: ‘post’, ‘get’
- url: ‘www.foo.com’
- 三个可选参数
- async: true/false 是否异步
- username:string
- password: string
跨域请求的限制
- 无法接收和发送cookies, 那么如果有需要进行用户验证的话可选参数中的username和password, 请避免使用明文, 可加密后传输
- 无法通过setRequestHeader()自定义头部, 要不然服务器要啥origin我传啥origin, 就不会有任何作用了.
- 调用getAllResponseHeaders()获取详细的响应头也只会返回一个空字符串
PreFlighted Request 预发请求
其中有一个细节需要注意的就是浏览器在发现是跨域请求的时候, 是不会先发送这个请求, 而实先发一个 OPTIONS 请求, 这个就是预发请求.将会发送以下头部:
Origin: www.bar.com
Access-Origin-Request-Method: GET
Access-Origin-Request-Header: ABC(可选)
发送请求后, 服务器决定是否接收请求并响应
Access-Control-Allow-Origin: www.bar.com
Access-Control-Allow-Method: GET, POST
Access-Control-Allow-Header: ABC
Access-Control-Max-Age: 3600
这里需要说一下的就是这个Access-Control-Max-Age, 这里规定了多少时间内可以不用再发Option去检测, 而是直接发送请求.所以preflight request的额外请求消耗只在第一次的时候发送.
还有一个特殊的响应头:Access-Control-Allow-Credentials: true/false
, 默认情况下跨域请求时部提供cookie, HTTP认证等凭据的. 这里不会影响发送的请求, 只会影响响应, 如果发送的请求携带了验证信息, 但是服务端的响应头里的该字段为false的话浏览器不会将响应交给JavaScript, 即xhr.responseText为空字符串.
注意, 如果将该字段设置为true之后, Access-Controle-Origin-Allow: *
是不允许的, 必须指定一个特定的域. 这还是为了安全着想.避免之前说的CSRF
你看, 跨域很简单, 但是需要服务端配合.如果使用express的话可以这样去配置:
// node
const 大专栏 记一次前端跨域的问题 - 礼锐的博客span class="nx">express
时间: 2024-09-30 06:26:08