跨域
什麼是跨域?
跨域是指一個域下的文檔或腳本試圖去請求另一個域下的資源,這裏跨域是廣義的。
廣義的跨域:
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://127.0.0.1:5501/6.正式課第六週/day4/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跨域
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>