不同域之间相互请求资源,就算作跨域,产生跨域的主要原因是因为同源策略。同源策略要求源相同才能正常进行通信,也就是协议、域名、端口都完全一致。但是请求跨域了,我们的请求也同样发了出去,只是浏览器拦截了响应,同时也说明跨域并不能完全阻止CSRF,因为请求发出去了。
如何跨域
常用的跨域方法有JSONP
, CORS
, postmessage
等
1. JSONP
原理:通过<script>
标签引入一个js文件,这个js文件被引入成功后执行我们在url参数中指定的函数,并且用参数的方式传入我们需要的json数据,jsonp需要服务器配合
原生
<script>
function getData (item) {
return item;
}
</script>
<script
type="text/javascript"
src="url"></script>
ajax
$.ajax({
url: "url",
type: "get",
dataType: "jsonp", // 请求方式
jsonpCallback: "handleCallback", // 回调函数
data: {}
})
vue
this.$https.jsonp("url", {
params: {},
jsonp: "handleCallback"
}).then((res) => {});
使用JSONP方式进行跨域,兼容性比较好,但是也存在缺点,比如:
- 支持GET请求,不支持POST请求
- 不能解决不同于的两个页面或iframe之间进行数据通信
- JSONP如果从带有恶意代码的不安全域中加载代码执行,会存在安全隐患
2. CORS
一整个CORS通信过程,都是浏览器自动完成,不需要用户的参与,和AJAX通信没有差别,所以,实现CORS通信的关键是服务器,只要后端实现了CORS接口,就可以实现跨域。服务端设置Access-Control-Allow-Origin就可以开启CORS,这个属性是用来设置哪些域名可以访问,如果设置了通配符,就代表所有域名都可以访问。
使用node中间件express示例
app.use((req, res, next) => {
// 允许跨域访问的域名
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Methods", "POST, GET, PUT, DELETE, OPTIONS");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Authorization, Access-Control-Allow-Credentials");
})
这个请求会验证Authorization
字段,我们只需要在回调中过滤掉options
方法即可。
虽然CORS使用简单而且安全,也支持POST方式,但是CORS还属于一种新型的跨域问题解决方案,存在兼容问题,目前只支持IE10以上。
3. document.domain
document.domain这种方式只能用于二级域名相同的情况下,通过js设置document.domain为主域就实现了同域
// 父窗口 // a.html
<iframe id="iframe" src="url/b.html"></iframe>
<script>
document.domain = "domain.html";
var str = "123";
</script>
// 子窗口 b.html
<script>
document.domain = "domain.html";
// 获取父窗口变量
console.log(window.parent.str);
</script>
4. postMessage
window.postMessage(message, origin)
;接受两个参数,第一个参数message
是要发送的信息,只能是字符串格式,第二个参数origin
限定域,也可以设置为通配符*
,
// 发送消息
window.parent.postMessage('message', 'url')
// 接收消息
window.addEventListener('message', event => {
var origin = event.origin || event.originalEvent.origin;
if (origin === 'url') {
console.log('ok');
}
})
5. window.name
在window
的生命周期内,所有的页面都共有一个window.name
,并且一直存在,不会因有新的页面被重置,每个页面对window.name
都有读写的权限。
- Vue解决跨域的方式
配置proxy
代理
module.exports = {
proxy: {
'/api': {
target: 'url', // 接口域名
secure: false, // 如果是https接口,需要配置这个参数
changeOrigin: true, //是否跨域
pathRewrite: {
'^/api': '' //路径重写
}
}
}
}
将这个文件引入到config
下的index.js
中将proxyTable
插入到dev对象中进行跨域使用
7. 后台跨域
一般有两种解决办法
1. 将后台返回的数据格式改为jsonp
2. 后台中添加header,将域名访问设为通配符*
,或者指定某个具体域名