同源策略
- 什么是源
- 两个url的 协议、域名、端口号 三者相同则是同源的
- 可以通过
window.originorlocation.originapi获取当前的源 - a.com 引用了 b.com/index.js 那么认为这个js文件运行在源a.com中
- 同源限制的情景
- iframe引入同源资源可以读写,引入非同源资源则只读(是否允许操作dom)
- ajax跨域请求会被浏览器拦截,XHR/Fetch请求
- dom操作,localStorage,cookie中的信息都不能被跨域获取到
- 标签引入js/css\img文件并没有跨域限制,因为引入的文件并不能被客户端的js获取
- 为什么要有同源限制
- 获取你的sessionID,localStorage中的隐私信息
- 获取你在iframe网页中输入的敏感信息
- 获取你在浏览器中储存的其他身份特征用与伪造请求
- 归根结底在于你发出的请求,在接收方难以区分发送者的身份
- chrome不会发送跨域请求,其他浏览器一般为拦截请求的结果
如何解决跨域
CORS
CORS全称跨域资源共享
- 服务器实现CORS,在相应头添加允许的请求来源特征来实现开放特定的跨源通讯。
Response headers[edit]
Access-Control-Allow-Origin//制定开放跨域请求的源站点
Access-Control-Allow-Credentials//设置是否允许浏览器发送cookie
Access-Control-Allow-Methods//指定可跨域请求的方法
Access-Control-Allow-Headers//指定headers
Access-Control-Expose-Headers//用于指定除了六个简单头字段外同意被获取的其他头字段
- 工作流程
- 为服务器加上了特定站点的跨域访问权限
- 这个相应头字段被加入到服务器返回的response header中
- 浏览器收到后,检查对应值是否与request中的Origin值匹配
- 浏览器校验成功则放行通信
- 简单请求与复杂请求
- 简单请求
- 指请求方式为HEAD,POST或GET,且请求头信息不超过blablabla
- 这种请求的cors实现就是在请求头中加入了Origin字段来表明请求来源,浏览器根据其值来判断是否接受本次请求。
- 复杂请求
- 不满足上述条件的请求,比如PUT和DELETE方法或Content-Type为application/json。
- 浏览器会发起一个option请求方法的预发请求,来询问服务器是否允许本次请求。
- 服务器接受预检请求,确定允许跨域请求时才会回应一个没有body的回应,其中头带有cros信息。
- 浏览器收到响应并作出是否要发送真实请求的响应。
- 简单请求
- cros跨域请求浏览器默认不会发送cookie等身份凭证,需要前端在请求中设置字段并后端让credentials为true。
Proxy代理
- 服务端代理
- 跨域验证发生在服务器,服务器之间的通讯并不受到同源策略的限制。
- 那么请求一个同域的服务器,由这个服务器代理转发你的请求并返回。
- 浏览器代理
- 将请求拦截转发到另一个服务器,由第三方服务器帮助处理请求,本质也可以理解为服务端代理。除了跨域也能用于解决一些防火墙等问题。
- 本地代理,将浏览器一些特定的请求转发到本地,由本地处理这个请求。本质不属于跨域通信的解决方式,一般用于开发测试,将线上部署的项目地址所请求的开发文件通过浏览器代理转为本地开发更新的文件,来做到在另一个线上域中开发项目。
JSONP
最古老的jq时代解决方法
- 因为浏览器对js文件的请求并不受到跨域的限制,所以可以将要相应的内容放入一个js回调函数中来实现跨语通信
- 一般流程为前端制定一个函数名,加载请求中,后端将相应的内容装入此函数名的函数,前端请求到js文件后调用这个函数名来拿到后端在其中装入到数据。
- 优点可能只有能兼容ie,但是由于时script标签请求所以只能知道请求成功与失败,且只支持get请求。
window.postMessage
- 一个H5的API,专门用于跨域页面间通讯。
- 接收两个参数,从参数中可以知晓大概的使用方法
- message:需要传递到内容
- targetOrigin:传递目的地,为目标页面到域名,也可以是通配符。
- 通过监听message事件来获取传递事件信息。
一些可以跨域但并不会真的用于跨域的方案
- document.domain
- 两个同一个一级域名下的页面,一同设置这个值为共有一级域名。
- 同时满足同协议同端口时,可以实现iframe情况下的子域互跨。
- window.name
- 当一个浏览器页面重新加载,它的name可以保持不变。
- iframe一个跨域页面,在这个页面中设置window.name,再吧iframe的页面换为同源页面来获取这个字段。
- WebSocket
- 属于是杀鸡用牛刀了,为了跨域通讯建立全双工的套接字协议链接