13161216443

您所在位置: 首頁> 學習課程> python培訓班 | Python Web 開發之 JWT 簡介

python培訓班 | Python Web 開發之 JWT 簡介

發布百知教育 來源:學習課程 2019-12-09

在之前的課程中,介紹過 Flask-Login 框架,它是基于 Session 和 Cookie 技術來實現用戶授權和驗證的,不過 Session 有很多的局限性,這一節介紹一種基于 token 的驗證方式 —— JWT (JSON Web Token),除了對 JWT 的概念講解之外,還有在 Flask 中簡單實踐

session 的局限性

基于 Session 的驗證過程大體是:服務器端有一個 Session 詞典,當用戶驗證登錄后,在詞典中為該用戶創建一個 Session 對象,在響應( response )中返回一個 Session id,當用戶下次請求時,攜帶 Session id,服務器從 Session 詞典中可以恢復出 Session 對象,以完成用戶的驗證,在用 Session id 從恢復出認證實體。

從 Session 驗證過程可以看出一些局限性:

  • 服務器橫向擴展很困難:因為 Session 只能存活在一個服務實例中,將用戶請求引導到其他服務器,將丟掉用戶的登錄狀態
  • 攜帶信息量少,恢復會話信息比較耗時:Session 認證后,客戶端得到 Session ID, 服務器無法從 Session ID 中得到更多信息,需要從數據庫、文件系統或緩存中取得用戶信息,比較耗時
  • 沒有統一標準:Session 由各個服務器框架自己實現,沒有統一標準,存在應用擴展困難的問題,特別加密方式,五花八門,有很大的安全隱患

token 簡介

為了解決 Session 的問題,有了 token 的驗證方式。

token 可以理解成票據,或者憑證,當用戶得到服務器的認證后,由服務器頒發,在之后的請求時攜帶,免去頻繁登錄。

token 不同于 Session 的地方:

  • 可以獨立于具體的服務器框架生成和校驗
  • 可以攜帶更多的信息,避免對持久層的查詢操作
  • 基于標準的算法可以由不同的節點完成驗證

為了利用好 token 的驗證機制,IEIT (互聯網工程任務組),制定了基于 JSON 數據結構的網絡認證方式 JWA(JSON Web Algorithms),還針對不同應用場景提出了具體協議,如 JWS、JWE、JWK 等,他們可以統稱為 JWT,即 Javascript Web Token。

理解 JWA

JWA 的全稱是 JSON Web Algorithms

JSON 是 Javascript 的語言的文本對象表示法,是一種獨立語言環境的數據結構表示,可以用網絡數據傳輸,在前面 RESTful 章節中,對 API 調用的返回數據格式就是 JSON。

Algorithms 本義是算法的意思,這里特指加密算法,也就是用 JSON 表示的數據,經過加密后在在服務器端和客戶段之間傳輸。

有了數據結構和加密算法的基礎,根據不同的應用場景,定義出了具體實現:

  • JWS(JSON Web Signature)對數據進行簽名的,用于防止數據被篡改,傳輸不敏感數據的情況
  • JWE(JSON Web Encryption)對數據做了加密的,用于傳輸敏感數據,具有更好的安全性
  • JWK(JSON Web Key)是通過密鑰對數據進行加密的方法,規定了相應的加密算法

JWT(JSON Web Token)上面 JWS、JWE 和 JWK 的總稱。

JWT 簡介

JWT Wiki 上的定義是:


JSON Web Token is an Internet standard for creating JSON-based access tokens that assert some number of claims.

大致意思是,JWT 是用基于 JSON 數據結構的生成包含了一些權限聲明的網絡訪問憑證的網絡標準

數據結構

JWT 由 Header、Payload 和Signature,三部分組成,像這樣的形式:



eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.

eyJpc3MiOiJBdXRobGliIiwic3ViIjoiMTIzIiwibmFtZSI6ImJvYiJ9.

cBo6e7Uss5__16mlqZECjHJSKJDdyisevDP5cUGvJms

換行符只是為了展示用,實際 token 中不包括換行符

Header

用于指定采用的加密算法,以及 JWT 采用的形式類型,例如:

{

    "alg" : "HS256",    
    "typ" : "JWT"
    
}


  • alg 指定前面所用的算法,默認為 HmacSHA256 簡寫為 HS256,還有 HS384、RS256 等
  • typ 是指令牌的類型,JWT 令牌的類型為 JWT

Payload

用于攜帶一些信息,例如用戶名,過期時間 等等,例如:


    {  
    
    "sub": "1234567890",  
    
    "name": "John Doe",  
    
    "admin": true
    
    }


    JWT 標準定義了 7 個字段:



    python培訓班


    這些字段有實現這自由選取,也可以加入其他自定義字段


    Signature

    首先,需要指定一個密鑰(secret)。密鑰很重要,需要嚴格保密

    然后,使用 Header 里面指定的簽名算法(默認是 HMAC SHA256),按照下面的公式產生簽名:


    HMACSHA256(
    
      base64UrlEncode(header) + "." +
      
      base64UrlEncode(payload),
      
        secret
        
      )


    即先將 header 和 payload 分別做 base64url 編碼, 然后用 . 將他們連接成一個字符串,用加密算法,使用密鑰 secret, 得到的加密結果就算簽名


    Base64URL 編碼字符集是 Base64 字符集的子集
    = 被省略、+ 替換成 -,/ 替換成_
    因為 token 可能通過 URL 進行傳輸,而=、+、/ 在 URL 中有特殊含義

    驗證

    當客戶端發送請求時將 token 送到服務器端,可以用和簽名同樣的方式,重新計算一次簽名,如果和客戶端送過來的簽名一致,說明 token 沒有被篡改,如果不一致,說明 token 已被篡改,不安全了。

    由此可見,用于做簽名的密鑰 secret 很重要,一旦泄漏,將無法鑒別 token 的真偽

    JWT 應用

    關于 Python 的 JWT 實現不止一個,不同的庫,不同的實現方式層出不窮,今天要講解的是 Python 的 Authlib 庫,它是一個大而全的 Python Web 驗證庫支持多種 Python 框架

    Authlib 的 JWT

    Authlib 是構建 OAuth 和 OpenID 安全連接服務器的終極 Python 庫,包括了 JWS, JWE, JWK, JWA, JWT

    Authlib 功能強大而豐富,今天我們只了解他的 JWT 部分,之后在介紹基于第三方認證的 OAuth 技術時還會進一步講解

    安裝

    使用 pip 安裝

    pip install Authlib

    如果一切正常,可以導入 Authlib 模板,例如,引入 jwt :

    >>> from authlib.jose import jwt
    
    >>>


    小試牛刀

    JWT 是服務器端的機制,所以可以在命令行中做測試

    生成 token










    >>> from authlib.jose import jwt
    
    >>> header = {'alg': 'HS256'}
    
    >>> payload = {'iss': 'Authlib', 'sub': '123', 'name': 'bob'}
    
    >>>secret='123abc.'
    
    >>>token=jwt.encode(header,payload,secret)
    
    >>> print(token)b'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
    
    eyJpc3MiOiJBdXRobGliIiwic3ViIjoiMTIzIiwibmFtZSI6ImJvYiJ9.
    
    cBo6e7Uss5__16mlqZECjHJSKJDdyisevDP5cUGvJms'


    • 導入 jwt 模塊
    • 定義 header,并且設置簽名算法為 HS256
    • 定義 payload,作為傳輸信息
    • 定義 secret,注意這里只是方便演示,實際項目中最好是隨機生成,并妥善保存
    • 使用 jwt 的 encode 方法,生成 token,encode 方法一次性實現了所有關于 JWT 協議的定義
    • 打印出 token,可見,被 . 分隔為三部分,前兩部分是 header 和 payload的 Base64Url 編碼,最后一部分是 簽名

    解碼 token

    接上面的環境

    >>> claims = jwt.decode(token, secret)
    
    >>> print(claims)
    {'iss': 'Authlib', 'sub': '123', 'name': 'bob'}
    
    >>> print(claims.header)
    
    {'alg': 'HS256', 'typ': 'JWT'}
    
    >>> claims.validate()
    
    >>>


    • 用 jwt 模塊的 decode 方法,利用 secret 對 token 進行解碼,如果簽名正確,就會得到解碼內容,解碼對象是 authlib.jose.JWTClaims 類的實例
    • 打印出解碼內容,可以看到和生成 token 時的 payload 內容一致
    • 打印出 header,可以看到 typ 為JWT,即使用默認值
    • validate 方法用于檢驗 token 的有效性,比如:是否過期、主題是否一致,是否沒到生效時間等等,也可以針對每種情況單獨做驗證,例如validate_exp 可用檢驗是否過期

    雖然 JWT 理論很繁瑣,但 Authlib 庫提供了簡潔的方法,讓開發應用變得更高效

    與客戶端交互

    JWT 之所有流行,有個重要原因是可以支持多種客戶端,例如 瀏覽器和 app,JWT 標準規定,一般情況下,客戶端需要將 token 放在 Http 請求的 Header 中的 Authorization 字段中,舉個例子:



    GET /resource HTTP/1.1
    
         Host: server.example.com 
         
             Authorization: Bearer mF_9.B5f-4.1JqM



    • 用 GET 方式請求 /resource ,在 Header 中添加了 Authorization 字段

    • 不能直接將 token 作為 Authorization的值,必須有類型聲明,這里是Bearer

    Bearer 表示這個 token 是認證服務器生成的,用來做身份識別的,除此之外,IEIT 還定義了其他 認證類型,如 BisicDigest,可以簡單理解成 Bearer 就是 JWT 的認證類型

    除了通過 Http Header 類攜帶 token 之外,還可以通過 POST 請求主體,以及 URL 中的 querystring 來向服務器發送 token,這兩種情況下,需要使用 access_token 字段來表示 token

    JWT 標準建議使用 Header 方式,除非 Header 無法使用時才考慮其他方式


    Flask JWT

    Authlib 主要的用途在打造一個 OAuth 應用,對于單獨做 JWT 的實踐有些麻煩,因此我們用 flask-jwt 框架,做 JWT 的實踐。

    flask-jwt 和之前講述的 flask-login 用法很像,是基于 JWT 的認證的框架,提供和很多方便實踐的特性

    安裝 flask-jwt


    pip install Flask-JWT


    創建應用

    為了簡單,將所有代碼放在 app.py 中:



    from flask import Flaskfrom flask_jwt import JWT, jwt_required, current_identityfrom werkzeug.security import safe_str_cmp
    # User 類,用于模擬用戶實體class User(object):    def __init__(self, id, username, password):        self.id = id        self.username = username        self.password = password
       def __str__(self):        return "User(id='%s')" % self.id
    # User 實體集合,用于模擬用戶對象的緩存users = [    User(1, 'user1', 'abcxyz'),    User(2, 'user2', 'abcxyz'),]
    username_table = {u.username: u for u in users}userid_table = {u.id: u for u in users}
    # 獲取認證的回調函數,從 request 中得到登錄憑證,返回憑證所代表的 用戶實體def authenticate(username, password):    user = username_table.get(username, None)    if user and safe_str_cmp(user.password.encode('utf-8'), password.encode('utf-8')):        return user
    # 通過 token 獲得認證主體的回調函數def identity(payload):    user_id = payload['identity']    return userid_table.get(user_id, None)
    app = Flask(__name__)app.debug = Trueapp.config['SECRET_KEY'] = 'super-secret'
    jwt = JWT(app, authenticate, identity)  # 用 JWT 初始化應用
    @app.route('/protected', methods= ["GET", "POST"])  # 定義一個 endpoint@jwt_required()  # 聲明需要 token 才能訪問def protected():    return '%s' % current_identity  # 驗證通過返回 認證主體
    if __name__ == '__main__':    app.run()


    運行:


    $ python app.py
    
     * Serving Flask app "app" (lazy loading)
     
      * Environment: production   
      
      WARNING: This is a development server. Do not use it in a production deployment.   
      
      Use a production WSGI server instead. 
      
      * Debug mode: on * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit) 
      
      * Restarting with stat
      
       * Debugger is active! 
       
       * Debugger PIN: 566-326-511


    獲取 access_token

    flask-jwt 默認的獲取 token 的路由是/auth,請求方式是 POST,用 JSON 傳送用戶名密碼給服務器,例如:


    $ curl -X POST -H "Content-Type: application/json" localhost:5000/auth -d '{"username":"user1","password":"abcxyz"}'
    
    {
    
      "access_token": "
      
      eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.  eyJleHAiOjE...<省略>...VudGl0eSI6MX0.  
      
      M-shnDPAVdu...<省略>...LaH1EMIbrWjPto"
      
     }


    如果登錄憑證正確,則返回 access_token,可以看到被 . 分隔成三部分,即 JWT 的結構

    使用 access_token

    flask-jwt 默認通過 Header 傳送 token,為了和 OAuth 生成的 JWT 做區分,默認使用JWT 作為 token 的類型,例如,用上面生成的 JWT 請求 /protected


    curl -H "Authorization: jwt eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE...<省略>...VudGl0eSI6MX0.M-shnDPAVdu...<省略>...LaH1EMIbrWjPto" localhost:5000/protectedUser(id='1')


    如果 token 有效,則返回 token 對應的認證實體,這個例子中打印出了 user 實體

    總結

    本節課程講解了基于 token 驗證的 JWT,使用 Authlib 庫對 JWT 做了實踐練習,期望能幫助您更好的理解 JWT,最后通過 flask-jwt 模塊,實踐了 JWT 的驗證方式,和使用方式。在后續的課程中還會對目前流行的第三方認證框架 OAuth 做介紹,敬請期待。


    python培訓班:http://www.akpsimsu.com/python2019




    上一篇:0基礎入門Python數據分析、統計學、數據庫

    下一篇:應屆生去公司找個Java程序員的職位需要什么技能?

    相關推薦

    www.akpsimsu.com

    有位老師想和您聊一聊

    關閉

    立即申請