一、什么是同源策略岔冀?
同源策略限制從一個源加載的文檔或者腳本如何與來自另一個源的資源進行交互个榕。這是一個用于隔離潛在惡意文件的關(guān)鍵的安全機制啦逆。非同一個源的定義:
- 不同協(xié)議,https 和 http笛洛,如 https://www.baidu.com 和 http://www.baidu.com
- 不同端口夏志,如 http://127.0.0.1:8080 和 http://127.0.0.1:8090
- 不同域名,如 a.com 和 b.com
二苛让、什么是跨域沟蔑?跨域有幾種實現(xiàn)形式?
跨域就是不同源的資源之間的交互狱杰。正是因為同源策略瘦材,才會出現(xiàn)跨域這種問題》禄跨域的實現(xiàn)方式有:
- JSONP
- CORS
- 降域
- postMessage
三食棕、JSONP 的原理是什么
利用 html 的 script 標(biāo)簽可以引入其他 JS 資源而且不引起跨域問題朗和,原理是 JS 是被下載到當(dāng)前瀏覽器環(huán)境執(zhí)行,所以就不算跨域簿晓,就像平常通過 cdn 引入 jQuery 一樣眶拉。因此,我們可以通過這種方式憔儿,讓后端返回數(shù)據(jù)忆植,具體流程如下:
- 定義數(shù)據(jù)處理函數(shù) _fun
- 創(chuàng)建 script 標(biāo)簽,src 的地址執(zhí)行后端接口谒臼,最后加個參數(shù) callback = _fun
- 服務(wù)端在收到請求后朝刊,解析參數(shù),計算返還數(shù)據(jù)蜈缤,輸出 fun(data) 字符串拾氓。
- fun(data) 會放到 script 標(biāo)簽做為 js 執(zhí)行。此時會調(diào)用 fun 函數(shù)底哥,將 data 做為參數(shù)痪枫。
四、CORS 是什么叠艳?
CORS(Cross-Origin Resource Sharing)跨域資源共享奶陈,是一種允許 Web 應(yīng)用服務(wù)器進行跨域訪問控制機制,從而使跨域數(shù)據(jù)傳輸?shù)靡园踩M行附较。具體通過在響應(yīng)頭的 Header 里面加上 Access-Control-Allow-Origin
屬性吃粒,允許相應(yīng)的源地址訪問來實現(xiàn)。
五拒课、演示三種以上跨域的解決方式
1. JSONP
通過 node + express 來搭建本地服務(wù)器徐勃,實現(xiàn) JSONP 效果。 JS 代碼:
<!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>JSONP 演示</title>
</head>
<body>
<button id="getData">點擊獲取數(shù)據(jù)</button>
<div>
<h1>數(shù)據(jù)展示</h1>
<p id="dataShow"></p>
</div>
<script>
const getData = document.getElementById('getData')
getData.addEventListener('click', (e) => {
const jsonpTag = document.getElementById('jsonp')
if(jsonpTag){
jsonpTag.remove()
}
let scriptTag = document.createElement('script')
scriptTag.src = 'http://127.0.0.1:3000/jsonp?callback=jsonp'
scriptTag.id = 'jsonp'
document.querySelector('body').appendChild(scriptTag)
})
function jsonp(data) {
const dataShow = document.getElementById('dataShow')
const str = JSON.stringify(data)
dataShow.innerHTML = str
}
</script>
</body>
服務(wù)端代碼
const express = require('express')
const Mock = require('mockjs')
const router = express.Router()
router.use('/', (req, res, next) => {
console.log('jsonp')
next()
})
router.get('/', (req, res, next) => {
const data = Mock.mock({
'list|1-10': [{
'id|+1': 1,
'name|1-3': '@FIRST'
}]
})
const callback = req.query.callback
const resData = `${callback}(${JSON.stringify(data)})`
res.end(resData)
})
module.exports = router
實際演示
可以看到這請求和相應(yīng)不是同源的早像,因為端口不同僻肖。
2.CORS
客戶端代碼
<!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>CORS 演示</title>
</head>
<body>
<button id="getData">點擊獲取數(shù)據(jù)</button>
<div>
<h1>數(shù)據(jù)展示</h1>
<p id="dataShow"></p>
</div>
<script>
const getData = document.getElementById('getData')
const dataShow = document.getElementById('dataShow')
getData.addEventListener('click', (e) => {
const xhr = new XMLHttpRequest()
xhr.open('GET', 'http://127.0.0.1:3000/cors', true)
xhr.send()
xhr.addEventListener('load', (data) => {
if (xhr.status === 200) {
const resData = data.target.response
dataShow.innerHTML = resData
}
})
})
</script>
</body>
</html>
服務(wù)端代碼
const express = require('express')
const router = express.Router()
const Mock = require('mockjs')
router.use('/', (req, res, next) => {
console.log('cors')
res.append('access-control-allow-origin', 'http://127.0.0.1:8090')
// res.append('withCredentials', true)
next()
})
router.get('/', (req, res, next) => {
const data = Mock.mock({
'list|1-10': [{
'id|+1': 1,
'name|1-3': '@FIRST'
}]
})
res.json(data)
})
module.exports = router
實際演示
3. 降域
假設(shè)現(xiàn)在我有兩個域名 a.sub.com 和 b.sub.com,但實際上指向的是同一個 ip 地址和 端口卢鹦,盡管如此臀脏,因為域名不同,依舊是非同源冀自,為了解決這個問題揉稚,通過window.domain
來降域,解決跨域問題熬粗。
修改 hosts 文件
兩個 html 文件搀玖,里邊使用 iframe 演示。
<!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>網(wǎng)站 a</title>
<style>
iframe {
background-color: #eee;
}
</style>
</head>
<body>
<h1>使用降域?qū)崿F(xiàn)跨域</h1>
<input type="text" placeholder="http://a.sub.com:8090/a.html">
<iframe src="http://b.sub.com:8090/b.html" frameborder="0"></iframe>
<script>
document.domain = 'sub.com'
</script>
</body>
</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>網(wǎng)站 b.com</title>
</head>
<body>
<h1>這是網(wǎng)站 b</h1>
<script>
document.domain = 'sub.com'
</script>
</body>
</html>
在未使用window.domain
降域之前驻呐,在網(wǎng)站 a 里是無法訪問網(wǎng)站 b 的節(jié)點的灌诅,如下:
而加上之后: