本文已参与「新人创作礼」活动,一起开启掘金创作之路。
前端经常会遇到跨域的问题,今天学习并总结一下。
什么是跨域?
浏览器从一个域名的网页去请求另一个域名的资源时,域名、端口、协议任一不同,都是跨域。如
http://localhost:8080/web
http是协议;localhost是域名;8080是端口号。也就是说,这三个参数只要有一个不同,就会出现跨域问题。提到跨域问题就得先提到浏览器的同源策略。它是一种约定,是浏览器最核心也最基本的安全功能。同源策略会阻止一个域的javascript脚本和另外一个域的内容进行交互,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响,可以说Web是构建在同源策略基础之上的。下面给几个例子帮助理解:
满足同源策略,不跨域
- 浏览器地址 http://localhost:8090/
请求地址 http://localhost:8090/ //协议、域名、端口都相同
- 浏览器地址 http://10.0.1.1:80/
请求地址 http://10.0.1.1/ //协议、域名、端口都相同(http默认端口80)
- 浏览器地址 https://10.0.1.1/
请求地址 https://10.0.1.1:443/ //协议、域名、端口都相同(https默认端口443)
不满足同源策略,跨域
- 浏览器地址 http://localhost:8090/
请求地址 http://localhost:8091/ //协议、域名相同,端口不同
- 浏览器地址 http://localhost:8090
请求地址 https://localhost:8090 //域名、端口相同,协议不同
浏览器为了安全问题一般都限制了跨域访问,不允许跨域请求资源。
解决跨域问题的方法总结
1. 主域相同子域不同之document.domain跨域
- 主域名由两个或两个以上的字母构成,中间由点号隔开,整个域名通常只有1个点号。 百度的主域名是baidu.com。
- 子域是在主域名之下的域名,域名内容会有多个点号,如 www.baidu.com 浏览器是通过document.domain属性来检查两个页面是否同源,因此只要通过设置相同的document.domain,两个页面就可以共享Cookie。由于document.domain的修改是有限制的,只能设置为自身或者更高一级的域名,且主域要相同,此方案仅限主域相同,子域不同的跨域应用场景。
如域名是 www.test.com 可以给document.doamin赋值。但是,只能给当前域名或者它的基础域名赋值,才有效。
document.domain = 'test.com'; // 两个页面都设置
2. JSONP
利用script标签的src和img标签的src,或者说link标签的href它们没有被同源策略所限制,没有跨域限制。当需要通讯时,本站脚本创建一个script元素,利用script标签来执行跨域的javascript代码。通过这些代码,我们就能实现前端跨域请求数据(GET请求)。第三方产生的响应为json数据的包装(故称之为jsonp,即json padding)。
要提供一个回调函数来接收数据(函数名可约定,或通过地址参数传递)。为什么要定义callback呢?首先我们知道,这个get请求已经被发出去了,那么我们如何接口请求回来的数据呢,callback则可以帮我们做这件事。
jsonp的整个过程就类似于前端声明好一个函数,后端返回执行函数。执行函数参数中携带所需的数据。
JSONP的缺点:
- 具有局限性, 仅支持get方法
- 不安全,可能会遭受XSS攻击
3. CORS
CORS -- 跨站资源共享,它是跨域的官方解决方案,升级版的JSONP。普通跨域请求:只需服务器端设置Access-Control-Allow-Origin。带cookie跨域请求:前后端都需要进行设置。根据xhr.withCredentials字段判断前端是否带有cookie。服务器端对于CORS的支持,主要是通过设置Access-Control-Allow-Origin来进行的。如果浏览器检测到相应的设置,就可以允许Ajax进行跨域的访问。
4. Nginx
nginx代理跨域,实质和CORS跨域原理一样,通过配置文件设置请求响应头Access-Control-Allow-Origin…等字段。
代理跨域。反向代理跨域。实现原理类似于 Node 中间件代理,需要你搭建一个中转 nginx 服务器,用于转发请求。使用 nginx 反向代理实现跨域,是最简单的跨域方式。只需要修改 nginx 的配置即可解决跨域问题,支持所有浏览器,支持 session,不需要修改任何代码,并且不会影响服务器性能。
5. webpack本地代理
在webpack.config.js中利用 WebpackDevServer 配置本地代理,详情配置 devServer
6. websocket
是 HTML5 的一个持久化的协议,它实现了浏览器与服务器的全双工通信,同时也是跨域的一种解决方案。