用戶認證(Authentication)進化之路:由Basic Auth到Oauth2再到jwt

原文轉載自「劉悅的技術博客」https://v3u.cn/a_id_98

用戶認證是一個在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  

由 . 分爲三段,通過解碼可以得到:

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的區別及應用場景(面試向)

原文轉載自「劉悅的技術博客」 https://v3u.cn/a_id_98

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章