跨域
什么是跨域?
跨域是指一个域下的文档或脚本试图去请求另一个域下的资源,这里跨域是广义的。
广义的跨域:
1.) 资源跳转: A链接、重定向、表单提交2.) 资源嵌入: 、
参考链接:前端常见跨域解决方案(全)
1. 什么是同源策略?
同源策略(Same-origin Policy):为了保证浏览器的信息安全,浏览器采用同源策略,保证当前源中的资源只能在当前的源中使用;其他源如果需要使用当前源资源,需要特殊技术,这种 A 源访问 B 源的资源的通信称为跨域;
示例:
let xhr = new XMLHttpRequest();
xhr.open('GET', 'https://www.baidu.com/', true);
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && xhr.status === 200) {
console.log('xxx')
}
};
xhr.send();
- 以上请求会报错:
Access to XMLHttpRequest at 'https://www.baidu.com/' from origin 'http://localhost:63342'
has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header
is present on the requested resource.
当出现以上错误时说明你正在进行一个跨域的操作;
2. 同源策略的要求:
同源策略要求通信的两个源的协议、域名、端口号要相同,如果三者中任意一个不同就是不满足同源策略;不满足同源策略的通信就是跨域;
3. 常用的跨域解决方案:
- 通过jsonp跨域
- 跨域资源共享(CORS): Cross-Origin-Resource-Sharing: 需要目标域设置 Access-Control-Allow-Origin 头信息;
- postMessage跨域
- nginx代理跨域,nginx 是服务器应用程序,它可以接受客户端的请求,然后根据规则可以配置自动转发;
- window.name + iframe跨域
- WebSocket协议跨域
- document.domain + iframe跨域 //document.domain存放的是载入文档的服务器的主机名
- location.hash + iframe
- nodejs中间件代理跨域
- location.hash + iframe
- 服务端转发,因为同源策略只在客户端存在,在服务端是不存在的;所以可以由服务端转发请求;
- ajax 不能进行非同源的请求
1. JSONP跨域
JSONP 是一种常用的解决跨域的方式;
原理:利用 script 的 src 属性是不受同源策略约束的,可以访问不同服务器或者端口号下的数据
- JSONP 跨域原理: 利用了script的src属性是非同源策略的,就可以获取到非同源的数据,
然后当数据请求成功,再调用callback的回调函数;会把数据传递给这个回调函数的第一个参数;
- JSONP : 需要后端配合
- JSONP: 只能发送get请求,不能发送post请求;
- 提前声明一个叫做 fn 的函数,给 fn 设置一个形参;
- 在页面给 script 的 src 的指向的路径拼接一个 callback 属性,callback=fn;当浏览器解析到这个 script 标签时,会向 src 指向的路径发起 http 请求;
- 服务器收到这个请求后,会返回一个 fn (这里面是服务器返回的数据)
- fn({xxx}) 这个是让 fn 执行,小括号里面就是服务器发送给我们的数据
JS代码:
function fn(data) {
console.log(data);
}
HTML代码
<script src="http://matchweb.sports.qq.com/kbs/calendar?columnId=100000&callback=fn"></script>
简述
<script src="留言板/jquery-3.1.1.min.js"></script>
<script>
// 跨域: 是由于浏览器自身的同源策略导致的;浏览器只能访问同域下的数据或内容;
// 同域: 要求协议相同 域名相同 端口号相同;如果其中一个不相同,就会跨域;
// http://www.baidu.com:443/example/src/1.html
// http://www.baidu.com:888
// 只能访问该端口下的项目或者是数据;
// 浏览器: 同源策略;同源策略阻止一个域的脚本去请求另一个域的脚本进行交互;
// 前端都是通过ajax来获取数据的;
// ajax 不能访问不同源下的数据或内容
// $.ajax({
// // url是相对路径;相对于html文件
// // url:"http://1x7.0.0.1:5501/test/3.txt",//
// url:"http://matchweb.sports.qq.com/kbs/calendar?columnId=100000",
// type:"get",
// success:function(val){
// console.log(val);
// }
// })
// script : script 有个src属性,这个src属性是非同源的;可以支持跨域请求
function fn(data){
console.log(data);
}
// JSONP 跨域原理: 利用了script的src属性是非同源策略的,就可以获取到非同源的数据,
然后当数据请求成功,再调用callback的回调函数;会把数据传递给这个回调函数的第一个参数;
// JSONP : 需要后端配合
// JSONP: 只能发送get请求,不能发送post请求;
// cors window.name webSocket document.domain nginx
</script>
<script src="http://matchweb.sports.qq.com/kbs/calendar?columnId=100000&callback=fn">
</script>
jsonp封装
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<script>
// 跨域: 同源和非同源;
// 项目工作场景:官网:8000 管理系统:9000
// 同源和非同源 :
// 所有的请求的url都是这个样子
// http://127.0.0.1:8080/app/index.html
// 协议 域名 端口号
// 因为axios.
//axios.get("/user/list").then();
//axios.post("/customer/add".then())
// ajax 不能进行非同源的请求
// script的src是非同源的,可以从一个项目访问另一个项目,当数据成功返回之后,
会默认调用跨域地址后面的callback 回调函数
// 只能发送get请求;
// function fn(data){
// // data就接收到了后端的数据
// }
// //
// // jquery
// $.ajax({
// url:"user/login",
// dataType:"jsonp",
// success:function(data){
// }
// })
function jsonp(options) {
return new Promise(function (resolve, reject) {
window[options.cb] = function (data) {
resolve(data);
document.body.removeChild(script);
}
let script = document.createElement("script");
let str = `${options.url}?`;
for (let key in options.params) {
str += key + "=" + options.params[key] + "&"
}
str += "cb=" + options.cb;
script.src = str;
document.body.appendChild(script);
})
}
jsonp({
url: "/login",
params: {
user: 11223
},
cb: "fn"
}).then(function (data) {
// data就是成功获取的数据
})
</script>
<script src="http://www.baidu.com?callback=fn"></script>
</body>
</html>
2.cors跨域
- 跨域资源共享(CORS): Cross-Origin-Resource-Sharing: 需要目标域设置 Access-Control-Allow-Origin 头信息;
1.server.js
let express = require('express');
let app = express();
//利用cors解决跨域
app.use(function(req,res,next){
//设置允许的请求地址 * 代表所有的路径
res.header('Access-Control-Allow-Origin','*');
//设置允许的跨域方式
// res.header('Access-Control-Allow-Origin','GET,POST')
next()
})
//路由
app.get('/getData',function(req,res){
res.send('你很帅')
})
app.listen(8080);
console.log("成功");
2.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>
<script src='../../node_modules/axios/dist/axios.min.js'></script>
<script>
// 从5501向8080发送请求,被阻断了,跨域;
axios.get("http://127.0.0.1:8080/getData").then(function (data) {
console.log(data);
})
</script>
</body>
</html>
3.postMessage跨域
1.server.js
let express = require('express');
let app = express();
//解析静态资源文件
app.use(express.static(__dirname))
app.listen(8080,function(){
console.log('启动成功');
})
2.index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<iframe src="http://127.0.0.1:8080/message.html" id="frm"></iframe>
<input type="button" value="ok" onclick = 'run()'>
<script>
function run(){
let frm = document.getElementById('frm');
frm.contentWindow.postMessage({name:'快乐和滑稽'},'http://127.0.0.1:8080')
console.log(2);
}
</script>
</body>
</html>
3.message.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
//接收
window.addEventListener('message',function(e){
console.log(e.data);
})
</script>
</body>
</html>
demo–利用iframe嵌套父子窗口通信
父窗口:
<!--我是父窗口-->
<div class="parent" >
<iframe src="子窗口链接" id="iframe"></iframe>
</div>
<script>
//监听子窗口信息
window.addEventListener('message',function(event){
...
})
//父窗口给子窗口发消息,
document.getElementByID('iframe').contentWindow.postMessage(msg,'子窗口源');
</script>
子窗口
<!--我是子窗口-->
<div class="child"></div>
<script>
//子窗口给父窗口发消息
try {//放到trycatch里面,解决有些手机卡住报错问题
window.top.postMessage(msg,'父窗口源');
//嵌套一层使用window.top(parent),多层window.frameElement
//使用top而不是window,top指向iframe最顶层窗口
} catch (error) {
}
//监听父窗口信息
window.addEventListener('message',function(event){
...
})
</script>
注意:
父窗口给子窗口发信息,需要用iframe的contentWindow属性作为调用主体
子窗口给父窗口发的信息需要使用window.top,多层iframe使用window.frameElement
4. Nodejs中间件代理跨域
- webpack.config.js部分配置:
module.exports = {
entry: {},
module: {},
...
devServer: {
historyApiFallback: true,
proxy: [{
context: '/login',
target: 'http://www.domain2.com:8080', // 代理跨域目标接口
changeOrigin: true,
cookieDomainRewrite: 'www.domain1.com' // 可以为false,表示不修改
}],
noInfo: true
}}
iframe
- iframe :标签 可以在父页面中嵌套子页面
- iframe :src的地址就是嵌套子页面的地址
<!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>
<style>
html,
body {
height: 100%;
}
iframe {
border: none;
width: 100%;
height: 95%;
}
</style>
</head>
<body>
<!-- <iframe src="3.child.html"></iframe> -->//chilid子页面
<a href="https://baidu.com" target="iframeA">百度</a>
<a href="https://taobao.com" target="iframeA">淘宝</a>
<a href="https://jd.com" target="iframeA">京东</a>
<iframe src="https://baidu.com" name="iframeA"></iframe>
<script>
// iframe :标签 可以在父页面中嵌套子页面
// iframe :src的地址就是嵌套子页面的地址
</script>
</body>
</html>
align | - left - right - top - middle - bottom | 不赞成使用。请使用样式代替。 规定如何根据周围的元素来对齐此框架。 |
---|---|---|
frameborder | - 1 - 0 | 规定是否显示框架周围的边框。 |
height | - pixels - % | 规定 iframe 的高度。 |
longdesc | URL | 规定一个页面,该页面包含了有关 iframe 的较长描述。 |
marginheight | pixels | 定义 iframe 的顶部和底部的边距。 |
marginwidth | pixels | 定义 iframe 的左侧和右侧的边距。 |
name | frame_name | 规定 iframe 的名称。 |
sandbox | - “” - allow-forms - allow-same-origin - allow-scripts - allow-top-navigation | 启用一系列对 中内容的额外限制。 |
scrolling | - yes - no - auto | 规定是否在 iframe 中显示滚动条。 |
seamless | seamless | 规定 看上去像是包含文档的一部分。 |
src | URL | 规定在 iframe 中显示的文档的 URL。 |
srcdoc | HTML_code | 规定在 中显示的页面的 HTML 内容。 |
width | - pixels - % | 定义 iframe 的宽度。 |
<iframe width="160" height="180" frameborder="0" scrolling="no"
src="//chongzhi.jd.com/jdhome-czindex-2017.html">
</iframe>