前端跨域的那些事
跨域的理解
首先我们来创建两个服务,来模拟跨域的问题
首先来创建一个node的服务,这个服务使用了下面的test.html
const http = require('http') const fs = require('fs') http.createServer(function (request, response) { console.log('request come', request.url) const html = fs.readFileSync('test.html', 'utf8') response.writeHead(200, { 'Content-Type': 'text/html' }) response.end(html) }).listen(8888) console.log('server listening on 8888')
这个html中请求了另外一个服务的数据
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body> </body> <script> var xhr = new XMLHttpRequest() xhr.open('GET', 'http://127.0.0.1:8887/') xhr.send() </script> </html>
创建第二个服务来接收第一个服务发送过来的请求
const http = require('http') http.createServer(function (request, response) { console.log('request come', request.url) response.end('123') }).listen(8887) console.log('server listening on 8887')
分别启动两个服务,并且访问第一个服务中的test.html,test.html向第二个服务发送了请求后就出现了跨域的问题
这时候我们有两种做法,第一就是在第二个服务中设置允许跨域,在第二个服务中修改代码,添加Access-Control-Allow-Origin请求头
const http = require('http') http.createServer(function (request, response) { console.log('request come', request.url) response.writeHead(200, { 'Access-Control-Allow-Origin': '*' // 设置这个服务运行跨域,并且是允许所有域名下的请求都可以 }) response.end('123') }).listen(8887) console.log('server listening on 8887')
可以看到我们的请求成功了
其实不管我们有没有设置Access-Control-Allow-Origin这个请求头,浏览器都向第二个服务发送了请求,浏览器发送请求的时候并不知道第二个服务中是否是跨域的,所以浏览器还是发送了这个请求,并且接收返回内容
只是浏览器在接收到返回的内容中没有看到一个Access-Control-Allow-Origin,并且设置为允许的话,那么浏览器就会将这个返回的内容忽略掉,并且报错
那么上面是将 Access-Control-Allow-Origin请求头设置了*号,表示允许所有域名都可以访问,这样是不安全的,我们可以设置只允许某些域名下的请求才可以访问,比如下面代码设置了http://127.0.0.1:8888才可以访问
const http = require('http') http.createServer(function (request, response) { console.log('request come', request.url) response.writeHead(200, { 'Access-Control-Allow-Origin': 'http://127.0.0.1:8888', }) response.end('123') }).listen(8887) console.log('server listening on 8887')
第二种办法就是如果我们不设置第二个服务允许别的服务跨域请求的话,那么前端中可以使用JSONP来请求,只需要在test.html中修改代码
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body> 测试 </body> <script src="http://127.0.0.1:8887/"> // var xhr = new XMLHttpRequest() // xhr.open('GET', 'http://127.0.0.1:8887/') // xhr.send() </script> </html>
可以看到我们的请求是成功的,原理查看这篇文章:跨域通信—JSONP
为什么要跨域
跨域(默认get请求),通常情况下是说在两个不同的域名下面无法进行正常的通信,或者说是无法获取其他域名下面的数据,这个主要的原因是,浏览器出于安全问题的考虑,采用了同源策略,通过浏览器对JS的限制,防止恶意用户获取非法的数据。
比如这样的一个场景,恶意用户仿造一个银行的官网,在用户输入框中嵌套了银行的页面,如果是没有同源策略的限制,那么恶意用户则可以通过这样的一种方法来获取银行用户的卡号和登录密码,这样对于浏览器来说是没有安全性可言的。同时也可以有效的规避了大部分的XSS攻击(XSS攻击原理:通过向用户界面中注入script脚本,然后在脚本中获取用户的token等身份信息,然后将身份信息发送到恶意用户指定的地方,在正常用户还没有推出的时候,也就是token等身份信息还有效的时候,通过这些信息强制登录,将正常用户挤下系统。)
常见的几种跨域与使用场景
前端的跨域主要有:JSONP跨域、iframe跨域、window.name 跨域、document.domain 跨域、cookie跨域、postMessage跨域
JSONP跨域:JSONP跨域不是一种前端技术,而是程序猿创造的一种跨域方法,JSONP跨域,是一种简单的跨域方法,兼容性比较好
iframe 跨域:操作简便 、兼容性好,单纯的使用iframe跨域无法获取其他域名下的数据
window.name 跨域:必须与iframe配合使用,可以获取其他域名下的数据
document.domain 跨域:必须保证两个要跨域的对象是有一个关联域名,只针对两个跨域对象是关联域名,如果一个域名被攻击,那么另外一个域名也有安全问题
cookie跨域:这种方法跨域的兼容性好,由于需要一些额外的设置,所以删除cookie的时候比较繁琐,必须保证两个域名为关联域名
postMessage跨域:这种方法可以直接实现将数据从A站点传输到B站点,而且解除了cookie的限制和JSONP无法获取要传入的站点的信息,这个是HTML5新增加的特性,浏览器的兼容性不是很好,低版本的浏览器不支持
JSONP跨域
如果服务端没有设置允许跨域,那么前端代码使用JSONP发送请求即可
JSONP跨域主要的依据是利用一些HTML标签的“漏洞”,然后通过跨域的方式去调用这个在别的域名下面的脚本文件,JSONP跨域有script跨域
详情查看这篇文章:跨域通信—JSONP
项目中一般不会自己去写一个JSONP的请求,可以使用插件或者自己封装一个,这里有个比较常用的JSONP插件
jsonp插件
这是一个解决跨域问题的一个插件包,在项目中需要安装这个插件才能使用
npm install jsonp --save
CORS跨域
CORS跨域主要是在服务端进行一些设置,让前端的请求可以跨域
详情查看这篇文章:前后端通信—CORS(支持跨域)
postMessage解决跨域
详情查看这篇文章:HTML5新特性postMessage解决跨域