使用 Django REST Framework 和 Vue Nuxt 進(jìn)行身份驗(yàn)證
本文主要是關(guān)于數(shù)據(jù)如何在 Django + Nuxt 應(yīng)用中流動(dòng)的討論,項(xiàng)目源碼地址:https://gitlab.com/briancaffey/django-nuxt-starter
說明
這里我們使用一個(gè)簡單的 CRUD 應(yīng)用程序來進(jìn)行說明,該應(yīng)用具有兩個(gè)模型:Users 和 Posts,用戶可以使用電子郵件和密碼進(jìn)行登錄,以及創(chuàng)建、閱讀、更新和刪除博客文章(CRUD)。目前只做了 CRUD 的R(讀取):列出和查看博客文章,創(chuàng)建、更新和刪除將在以后添加,現(xiàn)在用戶必須先登錄才能查看帖子。
在 Django 中使用 Vue 的不同方法
就 Django 而言,以下是一些可以使用 Vue 的方法:
Vue 作為 jQuery 的替代品,用于在 Django 模板提供的視圖中進(jìn)行交互 構(gòu)建一個(gè)靜態(tài) Vue 應(yīng)用程序,并將其作為 Django 項(xiàng)目中的一組靜態(tài)資源,與其他普通 Django 模板視圖提供的路由一起使用 構(gòu)建一個(gè)使用 Django API(通常使用 Django REST Framework)的 Vue SPA,并通過內(nèi)容分發(fā)網(wǎng)絡(luò)(CDN)提供服務(wù) 使用 Vue 構(gòu)建使用 Django 作為 API 的 Electron 桌面應(yīng)用
在這些情況下,Vue 可以用作靜態(tài)資源(例如在 CDN 上提供SPA的情況下),也可以將 Vue 代碼包含在服務(wù)器的 HTML 響應(yīng)中,類似于 jQuery 的用法。
使用 Nuxt 的方法
Nuxt(帶有服務(wù)器端渲染或 SSR)是一個(gè)可以以幾種不同方式使用的框架,我將簡要討論使用 Nuxt 的三種方式。
靜態(tài)模式:此模式允許你編寫內(nèi)置在靜態(tài) HTML 中的 Vue 代碼,然后將該 HTML 部署到 CDN 或 NGINX 之類的 Web 服務(wù)器。開發(fā)人員(或 CI/CD流程)運(yùn)行命令為應(yīng)用程序中的每個(gè)頁面生成 HTML 文件,并且這些頁面在用戶訪問時(shí)按原樣提供。 SPA 模式:類似于使用 Vue CLI 啟動(dòng) Vue 項(xiàng)目時(shí)使用的模式。還可以像在靜態(tài)模式下一樣生成項(xiàng)目,但是生成的主要是在瀏覽器上執(zhí)行的 Javascript 代碼。 SSR 模式:服務(wù)器端渲染是我將在這里重點(diǎn)介紹的模式。與其他使用 Vue 的方式不同,此模式涉及 Node.js 服務(wù)器,它將處理我們的請(qǐng)求。例如,一個(gè) Web 請(qǐng)求 /posts被發(fā)送到我們的 Nuxt 服務(wù)器(一個(gè) Node.js 服務(wù)器進(jìn)程),Node.js 負(fù)責(zé)返回包含我們要顯示的所有博客帖子(或所有博客的分頁選擇)的 HTML。因此,Nuxt 應(yīng)用必須在返回完整呈現(xiàn)的 HTML 頁面之前向我們的 Django API 服務(wù)器發(fā)出請(qǐng)求/posts頁,然后用戶從 Nuxt 獲取頁面,讀取所有博客帖子,當(dāng)用戶點(diǎn)擊第2頁時(shí),我們直接從 Django API(而不是 Nuxt)請(qǐng)求第二頁數(shù)據(jù)。然后,用戶會(huì)看到一個(gè)簡短的加載動(dòng)畫,然后是使用 AJAX(通常使用fetch或 axios)加載的博客文章的第二頁。
Nuxt 的優(yōu)缺點(diǎn)
將 Nuxt 用于 SSR 會(huì)在應(yīng)用程序部署和我們的 Vue 代碼中引入很多復(fù)雜性,但是從靜態(tài) Vue SPA 遷移到 Nuxt 時(shí),后端 API 根本不需要更改。
僅 Django 就能為每個(gè)請(qǐng)求返回完全生成的,SEO 優(yōu)化的 HTML,但是隨著項(xiàng)目的不斷發(fā)展,使用 Vue 和 Django 模板構(gòu)建的應(yīng)用程序可能會(huì)難以使用,Django/DRF + Nuxt 方法可能更適合于具有專門的后端和前端團(tuán)隊(duì)的項(xiàng)目。另一個(gè)潛在的缺點(diǎn)是由于“雙重請(qǐng)求”而增加了等待時(shí)間,如果 Nuxt 服務(wù)和 Django 服務(wù)位于同一節(jié)點(diǎn)上,則此延遲可能問題不大。
工作流程

上圖主要針對(duì)基于 session 的身份驗(yàn)證進(jìn)行說明,重點(diǎn)是瀏覽器,Nuxt 服務(wù)器和 Django 服務(wù)器:用戶使用新的瀏覽器訪問該站點(diǎn),導(dǎo)航到“登錄”頁面,使用憑據(jù)登錄,然后訪問一個(gè)受保護(hù)的頁面:/posts;然后用戶關(guān)閉瀏覽器,然后直接返回/posts頁面。
這兩個(gè)操作聽起來很簡單,但它們涉及 Nuxt 的許多功能,這些包括:
asyncDatanuxtServerInit客戶端和服務(wù)器上的 Vuex 自定義插件的 axios
該圖尚未涉及的 Nuxt 的一些重要部分包括:
Nuxt auth 模塊 Nuxt 中間件等
/posts,重定向到/login,登錄,然后導(dǎo)航到/posts并查看博客文章
用戶導(dǎo)航到 http://domain.com/,該請(qǐng)求由 Nuxt 服務(wù)器處理。該動(dòng)作稱為 nuxtServerInit,這是一種特殊的 Vuex 動(dòng)作,如果在項(xiàng)目中定義store/index.js,則每次對(duì)Nuxt 服務(wù)器的請(qǐng)求都會(huì)調(diào)用一次(在瀏覽器中最初訪問頁面或刷新頁面時(shí))。nuxtServerInit在名為user的模塊中調(diào)用 Vuex 操作fetchData,此操作/api/account/向 Django 應(yīng)用中發(fā)出 GET 請(qǐng)求。/api/account/直接從 Docker 的 Nuxt 容器對(duì) Django 后端進(jìn)行 API 調(diào)用(backend:8000)如果請(qǐng)求是由匿名用戶發(fā)出的(沒有用戶登錄),則將403響應(yīng)返回到 Nuxt 服務(wù)器,并且未在 Vuex 存儲(chǔ)中設(shè)置任何帳戶數(shù)據(jù)。 由于用戶當(dāng)前未登錄,因此該請(qǐng)求返回403響應(yīng)。 authMiddleware(在 Nuxt 服務(wù)器上)/login根據(jù) Vuex 存儲(chǔ)中的 ?authenticated?值將用戶重定向到原始的請(qǐng)求/posts返回一個(gè)完全渲染的/login/頁面。用戶現(xiàn)在位于登錄頁面上。 created登錄頁面的 hook 向/api/login-set-cookie/發(fā)出 GET 請(qǐng)求。
10、11.此端點(diǎn)調(diào)用視圖的注解 @ensure_csrf_token。
當(dāng)響應(yīng)返回到瀏覽器時(shí),
csrftoken將在瀏覽器中進(jìn)行設(shè)置。$apiCall函數(shù)定義在plugins/axios.js中,并將 csrftoken cookie 添加到 API 請(qǐng)求的 X-CSRF-Token 的 Header 中,這對(duì)于需要 CSRF Token 的 POST 請(qǐng)求非常重要,當(dāng)用戶在登錄表單中填寫其 email 和密碼時(shí),將使用/api/login/`` 和 email/密碼作為憑據(jù)$apiCall` 函數(shù)。email 和密碼作為 POST 請(qǐng)求中的數(shù)據(jù)發(fā)送到
/api/login/。
15、16. /api/login/ URL 調(diào)用 login_view,它使用 django.contrib.auth 中兩個(gè)函數(shù):authenticate和login。authenticate從提供的email/密碼中獲取用戶,然后該login函數(shù)sessionid在響應(yīng)上設(shè)置一個(gè)HttpOnly 會(huì)話 cookie。
sessionid
當(dāng)/api/login/`請(qǐng)求成功返回時(shí),將在瀏覽器上自動(dòng)設(shè)置HttpOnly cookie 。當(dāng)此
/api/login/請(qǐng)求成功返回時(shí),將auth設(shè)置 Vuex 模塊中的值以跟蹤當(dāng)前用戶的登錄狀態(tài)。接下來,向
/api/account/發(fā)出 GET 請(qǐng)求。
20、21. 由于sessionid cookie 會(huì)自動(dòng)設(shè)置并隨請(qǐng)求一起發(fā)送,因此該請(qǐng)求將成功。
當(dāng)
/api/account/請(qǐng)求返回時(shí),用戶的賬戶信息被保存到user這個(gè) Vuex 模塊。此時(shí),客戶端可以自動(dòng)重定向到主頁,用戶帳戶頁面,儀表板等。現(xiàn)在登錄,用戶導(dǎo)航(再次通過 Vue 路由器)到
/posts,該頁面顯示所有博客文章的分頁視圖。此頁面具有
asyncData創(chuàng)建頁面組件并調(diào)度 Vuex 操作時(shí)調(diào)用的方法posts/fetchData。此 Vuex 操作向發(fā)出 GET 請(qǐng)求
/api/posts/。
26,27./api/posts/調(diào)用 ModelViewSet 并返回博客文章的分頁列表。
當(dāng) /api/posts/請(qǐng)求返回成功,博客文章的數(shù)據(jù)保存到blogVuex模塊。
用戶場(chǎng)景二:登錄的用戶打開新的瀏覽器窗口并重新訪問 /posts
用戶關(guān)閉其瀏覽器,然后打開一個(gè)新的瀏覽器窗口并導(dǎo)航到 /posts。nuxtServerInit像往常一樣被調(diào)用該 user/fetchData動(dòng)作被調(diào)用,此操作向發(fā)出 GET 請(qǐng)求/api/account/。/api/account/請(qǐng)求成功返回,會(huì)話 ID cookie 從瀏覽器傳遞到從 Nuxt 服務(wù)器向后端 API(/api/account/)發(fā)出的 API 請(qǐng)求。然后在 Vuex 用戶模塊上設(shè)置用戶帳戶數(shù)據(jù)。頁面的 asyncData方法/posts被調(diào)用。asyncData 分發(fā) Vuex 動(dòng)作posts/fetchData`posts/fetchData向/api/posts/發(fā)出 API 請(qǐng)求。/api/posts/ 請(qǐng)求由模型 Post 的 ModelViewSet 處理,該模型獲取博客帖子,然后在請(qǐng)求返回響應(yīng)(至 Nuxt服務(wù)器)時(shí)將其設(shè)置到 Vuex 存儲(chǔ)(在服務(wù)器上)。 異步數(shù)據(jù)獲取完成后(/posts 頁面為 nuxtServerInit 和 asyncData),將使用服務(wù)器上存儲(chǔ)的 Vuex 存儲(chǔ)數(shù)據(jù)來呈現(xiàn) HTML 頁面。Vuex 數(shù)據(jù)與呈現(xiàn)的 HTML 一起返回。 最后,用戶看到博客文章列表,該頁面“立即”加載;最初加載頁面后無需等待數(shù)據(jù)加載。
下一步
下一步是準(zhǔn)備在我有時(shí)間的時(shí)候?qū)⑵洳渴鸬缴a(chǎn)環(huán)境中,現(xiàn)在在本地設(shè)置運(yùn)行良好,并且我認(rèn)為它對(duì)于簡單的DigitalOcean Docker 集群部署也應(yīng)該很好,就像我在其他 Django + Vue 項(xiàng)目中所做的那樣。
我還想添加帖子的創(chuàng)建,更新和刪除功能,通過 API 調(diào)用改善錯(cuò)誤處理,添加表單驗(yàn)證,也許還可以使用 Jest 編寫一些測(cè)試??
原文鏈接:https://briancaffey.github.io/2021/01/01/session-authentication-with-django-django-rest-framework-and-nuxt/
