一、同源策略
同源策略(SOP 全称 Same origin policy):是浏览器的一种安全策略
所谓的同源策略,指 浏览器不允许非同源网址的访问
- 域名 完全相同
- 协议 完全相同
- 端口号 完全相同
- ( URL 中文件路径、锚点、查询字符串 等部分不同,不会影响访问 )
同源策略限制以下几种行为:
- Cookie、LocalStorage 和 IndexDB 无法读取
- DOM 和 Js对象无法获得
- AJAX 请求不能发送
如果缺少了同源策略,浏览器很容易受到XSS、CSFR等攻击
二、跨域
跨域: 就是非同源的网址 的正常访问(服务端没有跨域的需求)
跨域出现的错误提示:
Access-Control-Allow-Origin
具有跨域能力的标签:
<script>
、<img>
标签的src
属性具有跨域请求能力<link>
标签的href
属性<iframe>
标签
需要跨域的场景:
|URL | 说明 | 是否允许通信|
| — | — | — |
http://www.domain.com/a.js
http://www.domain.com/b.js
http://www.domain.com/lab/c.js |同一域名,不同文件或路径 | 允许
http://www.domain.com:8000/a.js
http://www.domain.com/b.js | 同一域名,不同端口 | 不允许,需跨域
http://www.domain.com/a.js
https://www.domain.com/b.js | 同一域名,不同协议 | 不允许,需跨域
http://www.domain.com/a.js
http://192.168.4.12/b.js | 域名和域名对应相同ip | 不允许,需跨域
http://www.domain.com/a.js
http://x.domain.com/b.js
http://domain.com/c.js | 主域相同,子域不同 | 不允许,需跨域
http://www.domain1.com/a.js
http://www.domain2.com/b.js | 不同域名 | 不允许,需跨域
三、跨域解决方案
- 概述:
- jsonp 跨域
- document.domain + iframe 跨域
- location.hash + iframe 跨域
- window.name + iframe 跨域
- postMessage 跨域
- 跨域资源共享(CORS)
- nginx 代理跨域
- nodejs中间件代理跨域
- WebSocket 协议跨域
1. jsonp 跨域
适用场景: 只能实现 GET 请求的跨域
原理: 应用
<script>
标签的src
属性 具有跨域请求能力,动态创建<script>
标签;将src
属性值定义为 需要跨域的 url;并将<script>
标签 添加到文档流中原生js 实现jsonp跨域:
<script>
var script = document.createElement('script');
script.type = 'text/javascript';
// 传参并指定回调执行函数为onBack
script.src = 'http://www.domain2.com:8080/login?user=admin&callback=onBack';
document.head.appendChild(script);
// 回调执行函数(服务器返回时,即执行该函数)
function onBack(res) {
alert(JSON.stringify(res));
}
</script>
- jquery 实现jsonp跨域:
- jquery 调用ajax方法时,发现
dataType: jsonp
;便动态创建<script>
标签
- jquery 调用ajax方法时,发现
$.ajax({
url: 'http://www.domain2.com:8080/login',
type: 'get',
dataType: 'jsonp', // 请求方式为jsonp
jsonpCallback: "onBack", // 自定义回调函数名
data: {}
});
- vue.js 实现jsonp跨域:
this.$http.jsonp('http://www.domain2.com:8080/login', {
params: {},
jsonp: 'onBack'
}).then((res) => {
console.log(res);
})
- 后端node.js 实现jsonp跨域:
var querystring = require('querystring');
var http = require('http');
var server = http.createServer();
server.on('request', function(req, res) {
var params = qs.parse(req.url.split('?')[1]);
var fn = params.callback;
// jsonp返回设置
res.writeHead(200, { 'Content-Type': 'text/javascript' });
res.write(fn + '(' + JSON.stringify(params) + ')');
res.end();
});
server.listen('8080');
console.log('Server is running at port 8080...');
2. document.domain + iframe 跨域
适用场景: 只能实现 主域相同、子域不同 的跨域
原理: 两个页面都通过js 强制设置document.domain为基础主域,从而实现了同域
缺陷: 允许主子域脚本进行通信,但不涉及 localStorage 、indexedDB 和 XMLHttpRequest 的共享
代码展现:
(1) 父窗口
http://www.domain.com/a.html
<iframe id="iframe" src="http://child.domain.com/b.html"></iframe>
<script>
document.domain = 'domain.com';
var user = 'admin';
</script>
- (2) 子窗口
http://child.domain.com/b.html
<script>
document.domain = 'domain.com';
// 获取父窗口中变量
alert('get js data from parent ---> ' + window.parent.user);
</script>