什麼是跨域
瀏覽器的同源策略:瀏覽器為確保資源安全,而遵循的一種策略,該策略對訪問資源進行了一些限制
https://www.w3.org/Security/wiki/Same_Origin_Policy
https://developer.mozilla.org/zh-CN/docs/Web/HTTP/CORS
1、發生跨域後會出現的問題:
1、限制DOM訪問
<!-- <iframe id="framePage" src="./demo.html"></iframe> -->
<iframe id="framePage" src="https://www.baidu.com"></iframe>
<script type="text/javascript" >
function showDOM(){
const framePage = document.getElementById('framePage')
console.log(framePage.contentWindow.document) //同源的可以獲取,非同源的無法獲取
}
</script>
2、限制cookie訪問(實際上dom無法訪問,cookie也自然無法訪問了)
<iframe id="baidu" src="http://www.baidu.com" width="500" height="300"></iframe>
<script type="text/javascript" >
// 訪問的是當前源的cookie,並不是baidu的cookie
console.log(document.cookie)
</script>
3、限制Ajax獲取資料(請求可以發出,但是無法獲取源B的響應資料)
const url = 'https://www.toutiao.com/hot-event/hot-board/?origin=toutiao_pc'
let result = await fetch(url)
let data = await result.json();
console.log(data)
2、注意點
1、跨域限制僅存在瀏覽器端,服務端不存在跨域限制
2、即使跨域了,Ajax 請求也可以正常發出,但響應資料不會交給開發者
3、<link>
、<script>、
...... 這些標籤發出的請求也可能跨域,只不過瀏覽器對標籤跨域不做嚴格限制,對開發幾乎無影響
跨域的解決方案
1、解決方案一:CORS
CORS 全稱:Cross-Origin Resource Sharing(跨域資源共享),是用於控制瀏覽器校驗跨域請求的一套規範,伺服器依照 CORS 規範,新增特定響應頭來控制瀏覽器校驗,大致規則如下:
● 伺服器明確表示拒絕跨域請求,或沒有表示,則瀏覽器校驗不透過。
● 伺服器明確表示允許跨域請求,則瀏覽器校驗透過
(1)處理簡單請求
簡單請求
- 請求方法為:GET、HEAD、POST
- 請求頭的Content-Type的值只能是以下三種:
● text/plain
● multipart/form-data
● application/x-www-form-urlencoded
伺服器響應時候,新增Access-Control-Allow-Origin
響應頭,宣告允許某個源發起跨域請求,瀏覽器校驗透過
服務端核心程式碼(以express框架為例)
// 處理跨域中介軟體
function corsMiddleWare(req,res,next){
// 允許 http://127.0.0.1:5500 這個源發起跨域請求
// res.setHeader('Access-Control-Allow-Origin','http://127.0.0.1:5500')
// 允許所有源發起跨域請求
res.setHeader('Access-Control-Allow-Origin','*')
next()
}
// 配置路由並使用中介軟體
app.get('/',corsMiddleWare,(req,res)=>{
res.send('hello!')
})
(2)處理複雜請求
複雜請求:不是簡單請求的請求就是複雜請求,比如application/json
複雜請求會自動傳送預檢請求
解決方案
第一步:伺服器先透過瀏覽器的預檢請求,伺服器需要返回如下響應頭:
第二步:處理實際的跨域請求(與處理簡單請求跨域的方式相同)
服務端核心程式碼
// 處理預檢請求
app.options('/students', (req, res) => {
// 設定允許的跨域請求源
res.setHeader('Access-Control-Allow-Origin', 'http://127.0.0.1:5500')
// 設定允許的請求方法
res.setHeader('Access-Control-Allow-Methods', 'GET')
// 設定允許的請求頭
res.setHeader('Access-Control-Allow-Headers', 'school')
// 設定預檢請求的快取時間(可選)
res.setHeader('Access-Control-Max-Age', 7200)
// 傳送響應
res.send()
})
// 處理實際請求
app.get('/students', (req, res) => {
// 設定允許的跨域請求源
res.setHeader('Access-Control-Allow-Origin', 'http://127.0.0.1:5500')
// 隨便設定一個自定義響應頭
res.setHeader('abc',123)
// 設定允許暴露給客戶端的響應頭
res.setHeader('Access-Control-Expose-Headers', 'abc')
// 列印請求日誌
console.log('有人請求/students了')
// 傳送響應資料
res.send(students)
})
2、解決方案二:使用cors庫等
nodejs
// 配置cors庫
app.use(cors())
// cors中介軟體配置
const corsOptions = {
origin: 'http://127.0.0.1:5500', // 允許的源
methods: ['GET', 'POST', 'PUT', 'DELETE', 'HEAD', 'OPTIONS'], // 允許的方法
allowedHeaders: ['school'], // 允許的自定義頭
exposedHeaders: ['abc'], // 要暴露的響應頭
optionsSuccessStatus: 200 // 預檢請求成功的狀態碼
};
app.use(cors(corsOptions)); // 使用cors中介軟體
django
INSTALLED_APPS = [
'django.contrib.admin',
...
'django.contrib.messages',
'django.contrib.staticfiles',
# 處理跨域
'corsheaders',
]
MIDDLEWARE = [
...
'django.contrib.sessions.middleware.SessionMiddleware',
# 處理跨域
"corsheaders.middleware.CorsMiddleware",
...
]
3、解決方案三、JSONP
- JSONP 概述: JSONP 是利用了
<script>
標籤可以跨域載入指令碼,且不受嚴格限制的特性,可以說是程式設計師智慧的結晶,早期一些瀏覽器不支援 CORS 的時,可以靠 JSONP 解決跨域。 - 基本流程:
○ 第一步:客戶端建立一個<script>
標籤,並將其src屬性設定為包含跨域請求的 URL,同時準備一個回撥函式,這個回撥函式用於處理返回的資料。
○ 第二步:服務端接收到請求後,將資料封裝在回撥函式中並返回。
○ 第三步:客戶端的回撥函式被呼叫,資料以引數的形勢傳入回撥函式。
JavaScript核心程式碼
<button onclick="getTeachers()">獲取資料</button>
<script type="text/javascript" >
function callback(data){
console.log(data)
}
function getTeachers(url){
// 建立script元素
const script = document.createElement('script')
// 指定script的src屬性
script.src= 'http://127.0.0.1:8081/teachers'
// 將script元素新增到body中觸發指令碼載入
document.body.appendChild(script)
// script標籤載入完畢後移除該標籤
script.onload = ()=>{
script.remove()
}
}
</script>
jQuery 封裝的 jsonp
$.getJSON('http://127.0.0.1:8081/teachers?callback=?',(data)=>{
console.log(data)
})
4、配置代理解決跨域
4.1 自己配置代理伺服器
藉助http-proxy-middleware配置代理
const { createProxyMiddleware } = require('http-proxy-middleware');
app.use('/api',createProxyMiddleware({
target:'https://www.toutiao.com',
changeOrigin:true,
pathRewrite:{
'^/api':''
}
}))
4.2
基於nginx搭建代理伺服器,基於Vue等腳手架搭建代理伺服器(本質上是對4.1的封裝)