最近在逛網(wǎng)站看到 flask + vue.js 前后端分離,就開始做了個小 demo , 考慮到交互安全性的問題,就使用 Flask 設(shè)計 RESTful 的認證和前端交互數(shù)據(jù),廢話不哆嗦,如下看:
數(shù)據(jù)庫
為了讓給出的示例看起來像真實的項目,這里我將使用 Flask-SQLAlchemy 來構(gòu)建用戶數(shù)據(jù)庫模型并且存儲到數(shù)據(jù)庫中(這里我用的是 MYSQL ,你也可以用其他的!)
用戶的數(shù)據(jù)庫模型是十分簡單的。對于每一個用戶,username 和 password 將會被存儲:

出于安全原因,用戶的原始密碼將不被存儲,密碼在注冊時被散列后存儲到數(shù)據(jù)庫中。使用散列密碼的話,如果用戶數(shù)據(jù)庫不小心落入惡意攻擊者的手里,他們也很難從散列中解析到真實的密碼。
密碼散列
為了創(chuàng)建密碼散列,我將會使用 werkzeug.security庫,一個專門用于密碼散列的 Python 包。
from werkzeug.security import generate_passwod_hash, check_passwod_hash

password() 函數(shù)接受一個明文的密碼作為參數(shù)并且存儲明文密碼的散列。當(dāng)一個新用戶注冊到服務(wù)器或者當(dāng)用戶修改密碼的時候,這個函數(shù)將被調(diào)用。
verify_password() 函數(shù)接受一個明文的密碼作為參數(shù)并且當(dāng)密碼正確的話返回 True 或者密碼錯誤的話返回 False。這個函數(shù)當(dāng)用戶提供和需要驗證憑證的時候調(diào)用。
你可能會問如果原始密碼散列后如何驗證原始密碼的?
散列算法是單向函數(shù),這就是意味著它們能夠用于根據(jù)密碼生成散列,但是無法根據(jù)生成的散列逆向猜測出原密碼。然而這些算法是具有確定性的,給定相同的輸入它們總會得到相同的輸出。
用戶注冊
Flask 中的路由實現(xiàn)

參數(shù) username 和 password 是從請求中攜帶的 JSON 數(shù)據(jù)中獲取,接著驗證它們。
如果參數(shù)通過驗證的話,新的 User 實例被創(chuàng)建。username 賦予給 User,接著使用 hash_password 方法散列密碼。用戶最終被寫入數(shù)據(jù)庫中。
用戶注冊這一塊在命令行出現(xiàn)了問題,找不到問題所在,就用來 Postman 注冊了

返回的信息是成功的!

基于密碼的認證
現(xiàn)在我們假設(shè)存在一個資源通過一個 API 暴露給那些必須注冊的用戶。這個資源是通過 URL: /api/resource 能夠訪問到。
為了保護這個資源,我們將使用 HTTP 基本身份認證,但是不是自己編寫完整的代碼來實現(xiàn)它,而是讓 Flask-HTTPAuth 擴展來為我們做。
使用 Flask-HTTPAuth,通過添加 login_required 裝飾器可以要求相應(yīng)的路由必須進行認證:

但是,F(xiàn)lask-HTTPAuth 需要給予更多的信息來驗證用戶的認證,當(dāng)然 Flask-HTTPAuth 有著許多的選項,它取決于應(yīng)用程序?qū)崿F(xiàn)的安全級別。
能夠提供最大自由度的選擇就是選用 verify_password 回調(diào)函數(shù),這個回調(diào)函數(shù)將會根據(jù)提供的 username 和 password 的組合的,返回 True(通過驗證) 或者 Flase(未通過驗證)。Flask-HTTPAuth 將會在需要驗證 username 和 password 對的時候調(diào)用這個回調(diào)函數(shù)。
verify_password 回調(diào)函數(shù)的實現(xiàn)如下:

這里是用 curl 請求只允許注冊用戶獲取的保護資源:

如果登錄失敗的話,會得到下面的內(nèi)容:

基于令牌的認證
每次請求必須發(fā)送 username 和 password 是十分不方便,即使是通過安全的 HTTP 傳輸?shù)脑掃€是存在風(fēng)險,因為客戶端必須要存儲不加密的認證憑證,這樣才能在每次請求中發(fā)送。
一種基于之前解決方案的優(yōu)化就是使用令牌來驗證請求。
我們的想法是客戶端應(yīng)用程序使用認證憑證交換了認證令牌,接下來的請求只發(fā)送認證令牌。
令牌是具有有效時間,過了有效時間后,令牌變成無效,需要重新獲取新的令牌。令牌的潛在風(fēng)險在于生成令牌的算法比較弱,但是有效期較短可以減少風(fēng)險。
令牌的生成以及驗證將會被添加到 User 模型中,其具體實現(xiàn)如下:

generate_auth_token() 方法生成一個以用戶 id 值為值,’id’ 為關(guān)鍵字的字典的加密令牌。令牌中同時加入了一個過期時間,默認為十分鐘(600 秒)。
驗證令牌是在 verify_auth_token() 靜態(tài)方法中實現(xiàn)的。靜態(tài)方法被使用在這里,是因為一旦令牌被解碼了用戶才可得知。如果令牌被解碼了,相應(yīng)的用戶將會被查詢出來并且返回。
API 需要一個獲取令牌的新函數(shù),這樣客戶端才能申請到令牌:

注意:這個函數(shù)是使用了 auth.login_required 裝飾器,也就是說需要提供 username 和 password。
剩下來的就是決策客戶端怎樣在請求中包含這個令牌。
HTTP 基本認證方式不特別要求 usernames 和 passwords 用于認證,在 HTTP 頭中這兩個字段可以用于任何類型的認證信息?;诹钆频恼J證,令牌可以作為 username 字段,password 字段可以忽略。
這就意味著服務(wù)器需要同時處理 username 和 password 作為認證,以及令牌作為 username 的認證方式。verify_password 回調(diào)函數(shù)需要同時支持這兩種方式:

新版的 verify_password 回調(diào)函數(shù)會嘗試認證兩次。首先它會把 username 參數(shù)作為令牌進行認證。如果沒有驗證通過的話,就會像基于密碼認證的一樣,驗證 username 和 password。
如下的 curl 請求能夠獲取一個認證的令牌:

現(xiàn)在可以使用令牌獲取資源:

需要注意的是這里并沒有使用密碼。
OAuth 認證
當(dāng)我們討論 RESTful 認證的時候,OAuth 協(xié)議經(jīng)常被提及到。
那么什么是 OAuth?
OAuth 可以有很多的含義。最通常就是一個應(yīng)用程序允許其它應(yīng)用程序的用戶的接入或者使用服務(wù),但是用戶必須使用應(yīng)用程序提供的登錄憑證。我建議閱讀者可以瀏覽 OAuth 了解更多知識。
好了, 一點點的理解,希望可以幫到你!