跨域問題
【 0 】前言
?
? 同源策略(Same Origin Policy)是瀏覽器安全策略的重要組成部分,它限制了來自不同源的網頁之間的數據交互,以防止惡意攻擊。當一個網頁嘗試執行與它的源(即協議、域名和端口)不一致的請求時,瀏覽器會出于安全考慮攔截這個請求。
? 跨域資源共享(CORS, Cross-Origin Resource Sharing)是一個W3C規范,它允許某些跨源請求,如Ajax,從而克服了Ajax只能同源使用的限制。CORS需要瀏覽器和服務器同時支持,基本思想是使用自定義的HTTP頭來讓瀏覽器和服務器進行通信。
? 在Django項目中解決跨域問題,可以通過多種方法,包括手動編寫中間件以及使用第三方庫。你給出的示例展示了如何手動編寫一個CORS中間件,以及如何使用django-cors-headers
這個流行的第三方庫來簡化CORS的設置。
手動編寫CORS中間件:
? 中間件代碼示例中,通過向響應頭中添加適當的CORS相關頭部,允許來自任何源的請求("Access-Control-Allow-Origin": "*"
)。同時,對于OPTIONS請求(預檢請求),該中間件還允許所有的方法和頭部。
使用django-cors-headers
庫:
- 安裝庫:
pip install django-cors-headers
- 在
settings.py
中添加'corsheaders'
到INSTALLED_APPS
。 - 在
settings.py
的MIDDLEWARE
中添加'corsheaders.middleware.CorsMiddleware'
,并確保它在其他中間件之前(特別是在CommonMiddleware
之前)。 - 配置CORS選項。例如,設置
CORS_ORIGIN_ALLOW_ALL
為True
來允許所有源的請求,或者你可以指定一個允許源的列表。CORS_ALLOW_METHODS
和CORS_ALLOW_HEADERS
分別定義了允許的HTTP方法和頭部。
? 請注意,出于安全考慮,通常不建議在生產環境中設置CORS_ORIGIN_ALLOW_ALL
為True
,除非你完全理解可能的安全風險。在生產環境中,最好明確指定允許的源。
? 另外,值得注意的是,雖然CORS解決了瀏覽器端的跨域問題,但服務器端仍然需要驗證請求的有效性,以防止潛在的安全威脅,如CSRF攻擊。因此,即使啟用了CORS,也不應忽視服務器端的安全性措施。
-
xss:跨站腳本攻擊
-
csrf:跨站請求偽造
-
cors:跨域資源共享
# 同源策略(Same origin policy)是一種約定,它規定了 請求的url地址,必須與瀏覽器上的url地址處于同域上,也就是域名,端口,協議相同,如果不一致,請求會發送成功,后端會正常響應,但是瀏覽器會攔截# 瀏覽器對非同源請求返回的結果做了攔截
# 只要做前后端分離,就會出跨域# 解決跨域問題-CORS :跨域資源共享 咱們講的方式 向響應頭中加數據,允許跨域- 后端代碼處理- nginx代理-JSONP :利用有的標簽沒有跨域問題 script img-websocket:長鏈接,不存在跨域-前端代理:開發階段用,上線不用# cors如何解決跨域-需要服務端支持---》就是服務端再響應頭中加數據即可# CORS基本流程CORS請求分成兩類:簡單請求(simple request)和非簡單請求(not-so-simple request)簡單請求,只發送一次:非簡單請求發送兩次,第一次是OPTIONS預檢請求,第二次是真正的請求
# 什么是簡單,什么是非簡單
#請求方法是以下三種方法之一:HEADGETPOST
# HTTP的頭信息不超出以下幾種字段:AcceptAccept-LanguageContent-LanguageLast-Event-IDContent-Type:只限于三個值application/x-www-form-urlencoded、multipart/form-data、text/plain# 請求頭中帶了 token ,所有請求都是非簡單### 解決跨域---統一寫個中間件--》處理所有跨域
from django.utils.deprecation import MiddlewareMixin
class CorsMiddleWare(MiddlewareMixin):def process_response(self,request,response):if request.method=="OPTIONS":#可以加*response["Access-Control-Allow-Headers"]="*"res['Access-Control-Allow-Methods'] = '*'response["Access-Control-Allow-Origin"] = "*"return response# 第三方解決方案# 1、使用pip安裝pip install django-cors-headers#2、添加到setting的app中INSTALLED_APPS = (...'corsheaders',...)#3、添加中間件MIDDLEWARE = [ ...'corsheaders.middleware.CorsMiddleware',...]
4、setting下面添加下面的配置
CORS_ORIGIN_ALLOW_ALL = True
CORS_ALLOW_METHODS = ('DELETE','GET','OPTIONS','PATCH','POST','PUT','VIEW',
)CORS_ALLOW_HEADERS = ('XMLHttpRequest','X_FILENAME','accept-encoding','authorization','content-type','dnt','origin','user-agent','x-csrftoken','x-requested-with','Pragma','token'
)
【 1 】解釋
? Access-Control-Allow-Headers
、Access-Control-Allow-Methods
和 Access-Control-Allow-Origin
是三個與跨源資源共享(CORS)相關的 HTTP 響應頭。它們各自有不同的作用和使用方法。
1. Access-Control-Allow-Origin
作用:
- 用于指定哪些源(域名、協議和端口)有權限訪問該資源。
使用方法:
- 如果服務器希望允許來自特定源的請求,如
https://example.com
,可以返回以下響應頭:
Access-Control-Allow-Origin: https://example.com
- 如果服務器希望允許來自任何源的請求(這通常在生產環境中是不安全的),可以返回:
Access-Control-Allow-Origin: *
2. Access-Control-Allow-Methods
作用:
- 列出哪些 HTTP 方法(如 GET、POST、PUT、DELETE 等)允許跨源請求。
使用方法:
- 如果服務器只允許 GET 和 POST 方法,可以返回以下響應頭:
Access-Control-Allow-Methods: GET, POST
- 這個響應頭通常與預檢請求(OPTIONS 請求)一起使用,以檢查服務器是否允許跨源請求中使用的特定 HTTP 方法。
3. Access-Control-Allow-Headers
作用:
- 用于告知瀏覽器在跨域請求中,所允許的請求頭字段(Request Header)。
使用方法:
- 如果瀏覽器請求包括
Access-Control-Request-Headers
字段,則Access-Control-Allow-Headers
字段是必需的。它的值是一個逗號分隔的字符串,表明服務器支持的所有頭信息字段,不限于瀏覽器在“預檢”中請求的字段。 - 例如,如果服務器允許跨域請求時使用的請求頭字段是
Content-Type
和Authorization
,可以返回以下響應頭:
Access-Control-Allow-Headers: Content-Type, Authorization
總結
Access-Control-Allow-Origin
用于指定哪些源可以訪問資源。Access-Control-Allow-Methods
用于指定哪些 HTTP 方法允許跨源請求。Access-Control-Allow-Headers
用于告知瀏覽器在跨域請求中,所允許的請求頭字段。
? 這三個響應頭通常一起使用,以確保跨源請求的安全性和可訪問性。同時,它們也遵循 CORS 規范,確保瀏覽器和服務器之間的正確交互。
【 2 】跨域問題
【 1 】笨辦法
from rest_framework.views import APIView
from lufy.utils.utils_response import APIResponse class CoreView(APIView):def get(self, request, *args, **kwargs):# return APIResponse(name="shazi",password='123123132',token='asdas.da.dasd')# return APIResponse(results=[{"user":'132'},{"hobby":['music','running']}])return APIResponse(headers={"hobby":['music','running']})
<template><div class="home"><h1>首頁</h1><!-- 在這里你可以添加一些內容來顯示獲取到的數據 --><div v-if="dataFromServer"><!-- 使用 dataFromServer 的數據來渲染內容 --><!-- 假設返回的數據是一個對象,并且你想顯示其某個屬性,例如 message --><p>{{ dataFromServer.message }}</p></div></div>
</template><script setup>
import { reactive, onMounted } from 'vue';
import axios from 'axios';// 使用 reactive 來創建響應式的數據對象
const res = reactive({message: ''
});// 在 onMounted 生命周期鉤子中發送請求
onMounted(async () => {try {const response = await axios.get('http://127.0.0.1:8000/user/option/', {headers: {// token: 'asd.fas.sadas' // 在請求頭中添加自定義的 token}});res.message = response.data.message;// 這里可以處理響應的其他數據console.log(response);} catch (error) {console.error('Error fetching data:', error);// 在這里你可以選擇以某種方式通知用戶或處理錯誤}
});
</script><style scoped>
/* 你的樣式代碼 */
</style>
from rest_framework.views import APIView
from lufy.utils.utils_response import APIResponse
class CoreView(APIView):def get(self, request, *args, **kwargs):# # return APIResponse(headers={"hobby":['music','running']})# No 'Access-Control-Allow-Origin' header is present on the requested resource.return APIResponse(headers={"Access-Control-Allow-Origin": '*'})
-
Access-Control-Allow-Headers
-
用于告知瀏覽器在跨域請求中,所允許的請求頭字段
-
urls.py
from django.urls import path
from . import views
from .views import LoggerView, ExceptionView,ResponseView,CoreView,core_viewurlpatterns = [# 日志path('logg/', LoggerView.as_view(), name='home'),# 異常path('except/', ExceptionView.as_view()),# 響應方法path('ponse/', ResponseView.as_view()),# 跨域path('core/', CoreView.as_view()),path('option/', core_view),]
from rest_framework.views import APIView
from lufy.utils.utils_response import APIResponse def core_view(request):res = HttpResponse("ok")res['Access-Control-Allow-Origin'] = '*'if request.method =='OPTIONS':res['Access-Control-Allow-Headers'] = '*'return res
<script setup>
import { reactive, onMounted } from 'vue';
import axios from 'axios';// 使用 reactive 來創建響應式的數據對象
const res = reactive({message: ''
});// 在 onMounted 生命周期鉤子中發送請求
onMounted(async () => {try {const response = await axios.put('http://127.0.0.1:8000/user/option/', {headers: {token: 'asd.fas.sadas' // 在請求頭中添加自定義的 token}});res.message = response.data.message;// 這里可以處理響應的其他數據console.log(response);} catch (error) {console.error('Error fetching data:', error);// 在這里你可以選擇以某種方式通知用戶或處理錯誤}
});
</script>
def core_view(request):res = HttpResponse("ok")res['Access-Control-Allow-Origin'] = '*'if request.method =='OPTIONS':res['Access-Control-Allow-Headers'] = '*'res['Access-Control-Allow-Methods'] = '*'return res
# 注意注釋跨域403
MIDDLEWARE = ['django.middleware.security.SecurityMiddleware','django.contrib.sessions.middleware.SessionMiddleware','django.middleware.common.CommonMiddleware',# 'django.middleware.csrf.CsrfViewMiddleware','django.contrib.auth.middleware.AuthenticationMiddleware','django.contrib.messages.middleware.MessageMiddleware','django.middleware.clickjacking.XFrameOptionsMiddleware',
]
? 非簡單請求(non-simple requests)在CORS(跨源資源共享)中,指的是不滿足簡單請求條件的請求。簡單請求和非簡單請求的區分主要是基于HTTP請求的一些特性,而不僅僅是基于發送了幾次請求。
簡單請求需要滿足以下條件:
- 請求方法只能是以下幾種之一:HEAD、GET、POST。
- HTTP頭信息不超出以下幾種字段:
- Accept
- Accept-Language
- Content-Language
- Last-Event-ID
- Content-Type:只限于三個值
application/x-www-form-urlencoded
、multipart/form-data
、text/plain
? 如果HTTP請求不滿足以上條件,那么它就是一個非簡單請求。
? 非簡單請求在瀏覽器發送真正的請求之前,會首先發送一個預檢請求(preflight request),請求方法是OPTIONS。預檢請求會詢問服務器是否允許該跨域請求。服務器響應預檢請求時,需要包含一些CORS相關的響應頭信息,比如 Access-Control-Allow-Origin
、Access-Control-Allow-Methods
、Access-Control-Allow-Headers
等,來告知瀏覽器該跨域請求是否被允許。
? 所以,非簡單請求并不是看你發送了幾次請求,而是看請求的HTTP方法、頭信息等是否滿足簡單請求的條件。如果不滿足,瀏覽器就會發送預檢請求,這增加了一次額外的HTTP請求。
【 2 】自定義中間件
from rest_framework.views import APIView
from lufy.utils.utils_response import APIResponse
def core_view(request):res = HttpResponse("ok")return res
from django.urls import path
from . import views
from .views import core_viewurlpatterns = [# optionpath('option/', core_view),]
- 在utils/utils.
## 中間件###
from django.utils.deprecation import MiddlewareMixin
class CorsMiddleWare(MiddlewareMixin):def process_response(self,request,response):if request.method=="OPTIONS":#可以加*response["Access-Control-Allow-Headers"]="*"response['Access-Control-Allow-Methods'] = '*'response["Access-Control-Allow-Origin"] = "*"return response
MIDDLEWARE = ['utils.utils_middleware.CorsMiddleWare',
]
<template><div class="home"><h1>首頁</h1><!-- 在這里你可以添加一些內容來顯示獲取到的數據 --><!-- 使用 dataFromServer 的數據來渲染內容 --><!-- 假設返回的數據是一個對象,并且你想顯示其某個屬性,例如 message --><p>{{ res.message }}</p></div></template><script setup>
import { reactive, onMounted } from 'vue';
import axios from 'axios';// 使用 reactive 來創建響應式的數據對象
const res = reactive({message: ''
});// 在 onMounted 生命周期鉤子中發送請求
onMounted(async () => {try {const response = await axios.put('http://127.0.0.1:8000/user/option/', {headers: {token: 'asd.fas.sadas' // 在請求頭中添加自定義的 token}});res.message = response.data.message;// 這里可以處理響應的其他數據console.log(response);} catch (error) {console.error('Error fetching data:', error);// 在這里你可以選擇以某種方式通知用戶或處理錯誤}
});
</script><style scoped>
/* 你的樣式代碼 */
</style>
【 3 】第三方app解決
# 1、使用pip安裝
pip install django-cors-headers# 2、添加到setting的app中
INSTALLED_APPS = (...'corsheaders',...
)
# 3、添加中間件
MIDDLEWARE = [ ...'corsheaders.middleware.CorsMiddleware',...
]
# 4、setting下面添加下面的配置
CORS_ORIGIN_ALLOW_ALL = True
CORS_ALLOW_METHODS = ('DELETE','GET','OPTIONS','PATCH','POST','PUT','VIEW',
)CORS_ALLOW_HEADERS = ('XMLHttpRequest','X_FILENAME','accept-encoding','authorization','content-type','dnt','origin','user-agent','x-csrftoken','x-requested-with','Pragma','token',
)### 中間件源碼分析
if conf.CORS_ALLOW_ALL_ORIGINS and not conf.CORS_ALLOW_CREDENTIALS:response[ACCESS_CONTROL_ALLOW_ORIGIN] = "*"
if request.method == "OPTIONS":response[ACCESS_CONTROL_ALLOW_HEADERS] = ", ".join(conf.CORS_ALLOW_HEADERS)response[ACCESS_CONTROL_ALLOW_METHODS] = ", ".join(conf.CORS_ALLOW_METHODS)
INSTALLED_APPS = (...'corsheaders',...)
-
3、添加中間件
MIDDLEWARE = [ ...'corsheaders.middleware.CorsMiddleware',... ]
-
4、setting下面添加下面的配置(放在新建的common_settings)
CORS_ORIGIN_ALLOW_ALL = True CORS_ALLOW_METHODS = ('DELETE','GET','OPTIONS','PATCH','POST','PUT','VIEW', )CORS_ALLOW_HEADERS = ('XMLHttpRequest','X_FILENAME','accept-encoding','authorization','content-type','dnt','origin','user-agent','x-csrftoken','x-requested-with','Pragma','token', )