原文轉載自「劉悅的技術博客」v3u.cn/a_id_98前端
用戶認證是一個在web開發中亙古不變的話題,由於不管是什麼系統,什麼架構,什麼平臺,安全性是一個永遠也繞不開的問題web
在HTTP中,基本認證(Basic access authentication)是一種用來容許網頁瀏覽器或其餘客戶端程序在請求時提供用戶名和口令形式的身份憑證的一種登陸驗證方式。
雖然基本認證很是容易實現,但該方案建立在如下的假設的基礎上,即:客戶端和服務器主機之間的鏈接是安全可信的。特別是,若是沒有使用SSL/TLS(https)這樣的傳輸層安全的協議,那麼以明文傳輸的密鑰和口令很容易被攔截。該方案也一樣沒有對服務器返回的信息提供保護。
現存的瀏覽器保存認證信息直到標籤頁或瀏覽器被關閉,或者用戶清除歷史記錄。HTTP沒有爲服務器提供一種方法指示客戶端丟棄這些被緩存的密鑰。這意味着服務器端在用戶不關閉瀏覽器的狀況下,並無一種有效的方法來讓用戶註銷。面試
OAuth 是一個關於受權(authorization)的開放網絡標準。容許用戶提供一個令牌,而不是用戶名和密碼來訪問他們存放在特定服務提供者的數據。如今的版本是2.0版。
嚴格意義上來說,OAuth2不是一個標準協議,而是一個安全的受權框架。它詳細描述了系統中不一樣角色、用戶、服務前端應用(好比API),以及客戶端(好比網站或移動App)之間怎麼實現相互認證。
最後,重點介紹一下JWT,JWT是一種安全標準。基本思路就是用戶提供用戶名和密碼給認證服務器,服務器驗證用戶提交信息信息的合法性;若是驗證成功,會產生並返回一個Token(令牌),用戶可使用這個token訪問服務器上受保護的資源。算法
JWT 特色:
1 體積小,於是傳輸速度快
2 傳輸方式多樣,能夠經過URL/POST參數/HTTP頭部等方式傳輸
3 嚴格的結構化。它自身(在 payload 中)就包含了全部與用戶相關的驗證消息,如用戶可訪問路由、訪問有效期等信息,服務器無需再去鏈接數據庫驗證信息的有效性,而且 payload 支持爲你的應用而定製化。
4 支持跨域驗證,能夠應用於單點登陸。數據庫
JWT是Auth0提出的經過對JSON進行加密簽名來實現受權驗證的方案,編碼以後的JWT看起來是這樣的一串字符:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
複製代碼
由 . 分爲三段,經過解碼能夠獲得:django
1 Header頭部分頭部分簡單聲明瞭類型(JWT)以及產生簽名所使用的算法。{"alg":"AES256","typ":"JWT"}跨域
2 playload(載荷)中的Claims聲明部分是整個token的核心,表示要發送的用戶詳細信息。有些狀況下,咱們極可能要在一個服務器上實現認證,而後訪問另外一臺服務器上的資源;或者,經過單獨的接口來生成token,token被保存在應用程序客戶端(好比瀏覽器)使用。一個簡單的聲明(claim)的例子:{"sub":"1234567890","name":"John Doe","admin":true}瀏覽器
3 Signature簽名簽名的目的是爲了保證上邊兩部分信息不被篡改。若是嘗試使用Bas64對解碼後的token進行修改,簽名信息就會失效。通常使用一個私鑰(private key)經過特定算法對Header和Claims進行混淆產生簽名信息,因此只有原始的token才能於簽名信息匹配。這裏有一個重要的實現細節。只有獲取了私鑰的應用程序(好比服務器端應用)才能徹底認證token包含聲明信息的合法性。因此,永遠不要把私鑰信息放在客戶端(好比瀏覽器)。緩存
簽名的目的:簽名其實是對頭部以及載荷內容進行簽名。因此,若是有人對頭部以及載荷的內容解碼以後進行修改,再進行編碼的話,那麼新的頭部和載荷的簽名和以前的簽名就將是不同的。並且,若是不知道服務器加密的時候用的密鑰的話,得出來的簽名也必定會是不同的。
這樣就能保證token不會被篡改。安全
最後,咱們將上面拼接完的字符串用HS256算法進行加密。在加密的時候,咱們還須要提供一個密鑰(secret)。相似鹽
這裏在第三步咱們獲得 JWT 以後,須要將JWT存放在 client,以後的每次須要認證的請求都要把JWT發送過來。(請求時能夠放到 header 的 Authorization )
在web框架Django中的具體應用:
安裝pyjwt
pip3 install pyjwt
複製代碼
在用戶登陸成功後,生成一個token
import jwt
encoded_jwt = jwt.encode({'username':'admin','site':'https://v3u.cn'},'secret_key',algorithm='HS256')
複製代碼
將這個token交給前端,之後前端訪問任意接口都將在header裏帶着這個令牌(token),用來作認證,而後咱們確定不能每個視圖方法都作驗證,因此能夠利用裝飾器作一個統一用戶認證模塊
#定義驗證裝飾器
from django.http import JsonResponse
import jwt
def auth_required():
def decorator(view_func):
def _wrapped_view(self,request, *args, **kwargs):
try:
auth = request.META.get('HTTP_AUTHORIZATION').split()
except AttributeError:
return HttpResponse('沒權限')
try:
dict = jwt.decode(auth[1], settings.SECRET_KEY, algorithms=['HS256'])
username = dict.get('data').get('username')
except jwt.ExpiredSignatureError:
return JsonResponse({"status_code": 401, "message": "Token expired"})
except jwt.InvalidTokenError:
return JsonResponse({"status_code": 401, "message": "Invalid token"})
except Exception as e:
return JsonResponse({"status_code": 401, "message": "Can not get user object"})
return view_func(request, *args, **kwargs)
return _wrapped_view
return decorator
複製代碼
至此,一個簡單的jwt用戶認證方法就寫好了,至於jwt中的令牌存在客戶端的什麼位置呢?能夠參考這一篇文章來尋找答案:完全弄清楚session,cookie,sessionStorage,localStorage的區別及應用場景(面試向)
原文轉載自「劉悅的技術博客」 v3u.cn/a_id_98