跨域笔记

跨域笔记

这几天在联调的时候请求后端同学的电脑总是报错,之前开发环境都是配好了的所以就没有在意。这次遇到了就顺便学习下,这次的解决办法是在vue.config.js里配置porxy把对应的接口地址匹配到对应的后台同学的ip就能访问了。然后顺便找了篇比较全的文章学习了一下,顺着他的思路敲了一遍。原文地址:segmentfault

什么是跨域?

指一个域下的文档或脚本试图去请求另一个域下的资源。

  1. 资源跳转、a超链接、重定向、表单提交
  2. 资源嵌入 < link >、< scirpt >、< img >、< frame >
  3. 脚本请求 js发起的ajax请求、DOM和js对象的跨域操作

同源策略

协议+域名+端口号 一致。即便两个不同的域名指向同一个ip地址,也非同源。

跨域解决方案

  1. 通过jsonp跨域
  2. document.domain + iframe跨域
  3. location.hash + iframe
  4. window.name + iframe跨域
  5. postMessage跨域
  6. 跨域资源共享(CORS)
  7. nginx代理跨域
  8. nodejs中间件代理跨域
  9. WebSocket协议跨域

跨域资源共享 CORS

普通跨域请求:只服务端设置Access-Control-Allow-Origin即可,前端无需配置。在之前的项目的开发环境就用的这种模式。但是 若需要带cookie请求则需要前后端都设置。

1
2
3
4
5
6
7
// 原生ajax
xhr.withCredentials = true
// VUE
// axios设置
axios.defaults.withCredentials = true
// vue-resource
Vue.http.optins.credentials = true

若后台配置成功,前端跨域访问时是不会提示跨域报错的。

nginx代理跨域

  1. nginx配置解决iconfont跨域
    在nginx静态资源服务器中加入以下配置

    1
    2
    3
    location / {
    add_header Access-Control-Allow-Origin
    }
  2. nginx 反向代理接口跨域
    跨域原理:同源策略是浏览器的安全策略,不是HTTP协议的一部分。服务器调用端调用HTTP接口只使用了HTTP协议,不会执行JS脚本,不存在同源策略也就不存在跨域问题。
    实现思路:通过nginx配置一个代理服务器做跳板机。域名与domain1相同,端口不同。反向代理访问domain2接口,并且可以顺便修改cookie中domain信息,方便当前域cookie写入,实现跨域登录。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    server {
    listen 81;
    server_name www.domain1.com;
    location / {
    proxy_pass http://www.doamin2.com:8080; #反向代理
    proxy_cookie_domain www.domain2 www.domain1.com;
    index index.html index.html //???
    // 当用webpack-dev-server等中间件代理接口访问nginx,此时无浏览器参与,所以下面的配置可以不启用。
    add_header Access-Control-Allow-Origin http://www.domain1.com
    add_header Access-Control-Allow-Credentials true
    }
    }

NodeJS中间件代理跨域

用vue-cli 3.0 搭建的项目,在vue.config.js里配置devServer就行,简单省事。

1
2
3
4
5
module.exports = {
devServer: {
proxy: 'http//开发同学的IP地址.com'
}
}

WebSocket协议跨域

这个最近还没有用到,用到了再补充。

jsonp

我们把js、css,img等静态资源分离到另一台独立域名的服务器上,在html页面中再通过相应的标签从不同域名下加载静态资源,而被浏览器允许,基于此原理,我们可以通过动态创建script,再请求一个带参网址实现跨域通信。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//原生实现
var script = document.createElement('script');
script.type = 'text/javascript';

// 传参一个回调函数名给后端,方便后端返回时执行这个在前端定义的回调函数
script.src = 'http://www.domain2.com:8080/login?user=admin&callback=handleCallback';
document.head.appendChild(script);

// 回调执行函数
function handleCallback(res) {
alert(JSON.stringify(res));
}


//vue实现
this.$http.jsonp('http://www.domain2.com:8080/login', {
params: {},
jsonp: 'handleCallback'
}).then((res) => {
console.log(res);
})

document.domain + iframe跨域

此方案仅限主域相同,子域不同的跨域应用场景。
实现原理:两个页面都通过js强制设置document.domain为基础主域,就实现了同域。
父窗口:(http://www.domain.com/a.html)

1
2
3
4
5
<iframe id="iframe" src="http://child.domain.com/b.html"></iframe>
<script>
document.domain = 'domain.com';
var user = 'admin';
</script>

子窗口:(http://child.domain.com/b.html)

1
2
3
4
5
<script>
document.domain = 'domain.com';
// 获取父窗口中变量
alert('get js data from parent ---> ' + window.parent.user);
</script>

location.hash + iframe

实现原理: a欲与b跨域相互通信,通过中间页c来实现。 三个页面,不同域之间利用iframe的location.hash传值,相同域之间直接js访问来通信。

具体实现:A域:a.html -> B域:b.html -> A域:c.html,a与b不同域只能通过hash值单向通信,b与c也不同域也只能单向通信,但c与a同域,所以c可通过parent.parent访问a页面所有对象。
a.html (http://www.domain1.com/a.html)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<iframe id="iframe" src="http://www.domain2.com/b.html" style="display:none;"></iframe>
<script>
var iframe = document.getElementById('iframe');

// 向b.html传hash值
setTimeout(function() {
iframe.src = iframe.src + '#user=admin';
}, 1000);

// 开放给同域c.html的回调方法
function onCallback(res) {
alert('data from c.html ---> ' + res);
}
</script>

b.html (http://www.domain2.com/b.html)

1
2
3
4
5
6
7
8
9
<iframe id="iframe" src="http://www.domain1.com/c.html" style="display:none;"></iframe>
<script>
var iframe = document.getElementById('iframe');

// 监听a.html传来的hash值,再传给c.html
window.onhashchange = function () {
iframe.src = iframe.src + location.hash;
};
</script>

c.html (http://www.domain1.com/c.html)

1
2
3
4
5
6
7
<script>
// 监听b.html传来的hash值
window.onhashchange = function () {
// 再通过操作同域a.html的js回调,将结果传回
window.parent.parent.onCallback('hello: ' + location.hash.replace('#user=', ''));
};
</script>

感谢您的阅读。 🙏