flask_jwt_extended 4.0學習


1.前言

       在學習flask_jwt_extended插件的時候遇到許多問題,究其原因是因為版本問題,4.0以后的版本在語法上做了許多優化,而我是參考較低版本的flask_jwt_extended學習的,一開始不明白出問題的原因,所以參考了許許多多博友的思路和寫法,發現並沒有問題,后來想到去看官方的文檔,發現了原因所在,以下是4.0版本的api變化對照,更加詳細的變化請參官方文檔

flask_jwt_extended4.0版本API的變化

2.flask_jwt_extended的常規使用

1. create_access_token()用來創建令牌

2. get_jwt_identity()用來根據令牌取得之前的identity信息

3. jwt_required()這是一個裝飾器,用來保護flask節點(endpoint)

 1 from flask_jwt_extended import create_access_token
 2 from flask_jwt_extended import jwt_required
 3 from flask_jwt_extended import get_jwt_identity
 4 from flask_jwt_extended import JWTManager
 5 
 6 from flask import Flask
 7 from flask import jsonify
 8 from datetime import timedelta
 9 
10 app = Flask(__name__)
11 jwt = JWTManager(app)
12 
13 app.config['JWT_SECRET_KEY'] = 'hello@#$%&'  # 加密鹽值
14 app.config['JWT_ACCESS_TOKEN_EXPIRES'] = timedelta(hours=1)  # 設置token的有效時間,默認15分鍾
15 
16 @app.route('/login',methods=['POST'])
17 def login():
18     # 攜帶用戶標識生成token
19     access_token = create_access_token(identity={'id': 1, 'username': 'apple'})
20     return jsonify(access_token=access_token)
21 
22 @app.route('/index',methods=['GET'])
23 @jwt_required() # token認證,老版本不需要括號
24 def index():
25     # 獲取認證標識中的信息
26     userInfo = get_jwt_identity()
27     return jsonify(userInfo)
28 
29 if __name__ == '__main__':
30     app.run()

  注意:請求時,headers需要攜帶Authorization參數,其值為Bearer+空格 +token的字符串。

3.刷新token

       說道刷新token,我在學習的時候有些問題並沒有想明白,並不是對技術的疑問,而是不清楚為什么要這樣做。我了解到為了解決access_token過期問題,會設置在用戶登入的同時返回一個refresh_token用於access_token的刷新,而refresh_token的有效期的設置會比access_token的時間長,所以我疑問的是,如果說僅僅是解決過期時間的問題, 可以直接將 access_token 的過期時間延長就行了, 但它確是以刷新 token 的方式讓使用者以無感的方式延長 access_token 的有效期。

  我所查的資料無非是說:一,提高用戶體驗感,免登入;二、安全,token如果設置太長時間會不安全。但是筆者覺得直接去設置延長access_token的時間,和刷新token變相延長token的時間,本質並沒有不同,只不過后者是在一段時間內,換了多個token,但其用於刷新access_token的refresh_token為啥就不會造成不安全的因素了?這就好比是把access_token的時間給了refresh_token,而在用refresh_token去刷新access_token,饒了個圈子而已,哎..沒想明白...

 1 from flask_jwt_extended import create_access_token
 2 from flask_jwt_extended import create_refresh_token
 3 from flask_jwt_extended import get_jwt_identity
 4 from flask_jwt_extended import jwt_required
 5 from flask_jwt_extended import JWTManager
 6 
 7 from datetime import timedelta
 8 from flask import Flask
 9 from flask import jsonify
10 
11 app = Flask(__name__)
12 app.config['JWT_SECRET_KEY'] = 'hello@#$%&'  # 加密鹽值
13 app.config['JWT_ACCESS_TOKEN_EXPIRES'] = timedelta(hours=1)  # 設置access_token的有效時間
14 app.config["JWT_REFRESH_TOKEN_EXPIRES"] = timedelta(days=1)  # 設置refresh_token的有效時間
15 jwt = JWTManager(app)
16 
17 @app.route("/login", methods=["POST"])
18 def login():
19     access_token = create_access_token(identity={'user': 'apple', 'id': 1})
20     refresh_token = create_refresh_token(identity={'user': 'apple', 'id': 1})
21     return jsonify(access_token=access_token, refresh_token=refresh_token)
22 
23 # 攜帶refresh_token請求此接口,刷新獲的新的access_token
24 @app.route("/refresh", methods=["POST"])
25 @jwt_required(refresh=True) # 刷新token的裝飾器,這是最新的寫法
26 def refresh():
27     identity = get_jwt_identity()
28     access_token = create_access_token(identity=identity)
29     return jsonify(access_token=access_token)
30 
31 @app.route("/protected", methods=["GET"])
32 @jwt_required()
33 def protected():
34     current_user = get_jwt_identity()
35     return jsonify(current_user=current_user)
36 
37 if __name__ == "__main__":
38     app.run()

     生成token響應如下。在這里需要說一下的是access_token只能用於攜帶訪問,並不能用於刷新token;同理refresh_token只能用於刷新,不能用於攜帶請求獲取數據

4.自定義裝飾器

  @jwt_required()只能校驗當前請求的用戶是否攜帶token,如果需要校驗更多則可以自定義裝飾器

 1 from flask_jwt_extended import create_access_token
 2 from flask_jwt_extended import get_jwt
 3 from flask_jwt_extended import JWTManager
 4 from flask_jwt_extended import verify_jwt_in_request
 5 
 6 from functools import wraps
 7 from datetime import timedelta
 8 from flask import Flask
 9 from flask import jsonify
10 
11 app = Flask(__name__)
12 jwt = JWTManager(app)
13 app.config["JWT_SECRET_KEY"] = "hello@#$%&"
14 app.config['JWT_ACCESS_TOKEN_EXPIRES']=timedelta(hours=1)
15 
16 # 它驗證請求中是否存在JWT和請求用戶是否管理員
17 def admin_required():
18     def wrapper(fn):
19         @wraps(fn)
20         def decorator(*args, **kwargs):
21             verify_jwt_in_request()
22             claims = get_jwt()
23             if claims["is_administrator"]:
24                 return fn(*args, **kwargs)
25             else:
26                 return jsonify(msg="Admins only!"), 403
27         return decorator
28     return wrapper
29 
30 
31 @app.route("/login", methods=["POST"])
32 def login():
33     access_token = create_access_token(
34         identity={'id': 1, 'username': 'apple'},
35         additional_claims={"is_administrator": True}
36     )
37     return jsonify(access_token=access_token)
38 
39 
40 
41 @app.route("/protected", methods=["GET"])
42 @admin_required()  # 使用自定義裝飾器
43 def protected():
44     additional_claims = get_jwt()
45     return jsonify(claims=additional_claims)
46 
47 if __name__ == "__main__":
48     app.run()

這里有些需要說明的地方:

1、verify_jwt_in_request() 不返回任何東西。如果令牌解碼鏈中的任何事情失敗,它將返回一個適當的異常。也就是說它在這里的作用是為了校驗當前請求有沒有token,如果沒有則會返回一個異常信息,不能訪問被保護的節點,同樣的和@jwt_required()它們一樣有四個可選的參數:optional=False, fresh=False, refresh=False, locations=None,這里暫時不說,后面統一描述。

2、@wraps(fn)的作用,它來自from functools import wraps,它的作用是消除裝飾器帶來的副作用。因為在使用裝飾器的時候,其實函數名和函數的doc已經發生了變化,而加上它則可以和好的解決這些問題,這也是使用裝飾器的一個小技巧。

3、additional_claims參數的用法,我們知道除去存放基本的用戶的標識identity外,在access_token中還可能存放其他的信息,這個時候就可以使用這個參數了,它的作用和@jwt.additional_claims_loader裝飾器的作用是一樣的,都是將信息存儲到access_token中。但有些不同的是,該函數在create_access_token()函數被調用后使用。

4、get_jwt()用於獲取access_token存儲的信息,在之前的版本它是get_jwt_claims()

如果存放其它信息較多的情況下,則可以使用裝飾器的方式添加:

 1 @app.route("/login", methods=["POST"])
 2 def login():
 3     access_token = create_access_token(
 4         identity={'id': 1, 'username': 'apple'}
 5         # additional_claims={"is_administrator": True}
 6     )
 7     return jsonify(access_token=access_token)
 8 
 9 @jwt.additional_claims_loader
10 def add_claims_to_access_token(identity):#參數identity,就是access_token中的用戶標識,該參數必須填
11     return {
12         'is_administrator': True
13     }

5.常用參數描述

optional:
  描述:如果為True,即使是沒有jwt授權,也能夠允許訪問被保護的的節點,默認為False
1 @app.route('/index', methods=['GET'])
2 @jwt_required(optional=True)
3 def index():
4     current_user = get_jwt_identity()
5     if current_user:
6         return jsonify(msg='授權用戶訪問'), 200
7     else:
8         return jsonify(msg='未授權用戶訪問'), 200
fresh:
  描述:如果為True,只能讓"新鮮"的授權token訪問,如果是刷新后的token則不能訪問,該參數要結合create_access_token來使用,當我們創建access_token時,有一個
可選參數為fresh如果我們把它設為True,則代表它是"新鮮"token,換句話說就是只有用戶登入產生的token才具有"新鮮度"。默認為False。
 1 from flask_jwt_extended import create_access_token
 2 from flask_jwt_extended import create_refresh_token
 3 from flask_jwt_extended import get_jwt_identity
 4 from flask_jwt_extended import jwt_required
 5 from flask_jwt_extended import JWTManager
 6 
 7 from datetime import timedelta
 8 from flask import Flask
 9 from flask import jsonify
10 
11 app = Flask(__name__)
12 app.config['JWT_SECRET_KEY'] = 'hello@#$%&'  # 加密鹽值
13 app.config['JWT_ACCESS_TOKEN_EXPIRES'] = timedelta(hours=1)  # 設置access_token的有效時間
14 app.config["JWT_REFRESH_TOKEN_EXPIRES"] = timedelta(days=1)  # 設置refresh_token的有效時間
15 jwt = JWTManager(app)
16 
17 @app.route("/login", methods=["POST"])
18 def login():
19     access_token = create_access_token(identity={'user': 'apple', 'id': 1}, fresh=True)  # 開啟新鮮度模式
20     refresh_token = create_refresh_token(identity={'user': 'apple', 'id': 1})
21     return jsonify(access_token=access_token, refresh_token=refresh_token)
22 
23 @app.route("/refresh", methods=["POST"])  # 攜帶refresh_token請求此接口,刷新access_token
24 @jwt_required(refresh=True)
25 def refresh():
26     identity = get_jwt_identity()
27     access_token = create_access_token(identity=identity, fresh=False)  # 刷新不開啟新鮮度模式
28     return jsonify(access_token=access_token)
29 
30 @app.route("/protected", methods=["GET"])
31 @jwt_required(fresh=True)  # 只允許登入授權的用戶訪問,刷新授權不能在訪問
32 def protected():
33     identity = get_jwt_identity()
34     return jsonify(identity=identity)
35 
36 @app.route('/index')
37 @jwt_required()  # 無論登入授權還是,刷新授權都能訪問
38 def index():
39     return jsonify(msg='歡迎訪問')
40 
41 if __name__ == "__main__":
42     app.run()

  這里需要注意的是,刷新toekn視圖函數里面的create_access_token的"新鮮度"是不開啟的,否則無論是否刷新都能訪問,這就失去了參數本身的意義,筆者認為該參數有兩個作用,其一是,當每次用戶通過提供用戶名和密碼進行身份驗證時,他們都會收到一個可以訪問任何路由的新訪問令牌。但一段時間后,該令牌將不再被認為是新的,一些關鍵或危險的路由將被阻止,直到用戶再次驗證其密碼。所有其他路由仍將正常工作,即使用戶的令牌不再新鮮。例如,我們可能不允許用戶更改他們的電子郵件地址,除非他們有一個新的令牌,但我們允許他們正常使用我們的Flask應用程序的其余部分。其二,可以了防止refresh_token被盜后導致被他人惡意刷新token的危害的發生。

  另外在補充一點,create_access_token的"新鮮度"是可以設定時間的,如fresh=timedelta(seconds=60)把"新鮮度"保持60s的時間,超過這個時間則,失去"新鮮度"

refresh:
  描述:如果為True,只允許refresh_token訪問被保護的節點,如果為False,則access_token才可以訪問被保護的節點,默認為False,該參數用於刷新token時需要設置
1 # 攜帶refresh_token請求此接口,刷新access_token
2 @app.route("/refresh", methods=["POST"])
3 @jwt_required(refresh=True) #只允許刷新token訪問
4 def refresh():
5     identity = get_jwt_identity()
6     access_token = create_access_token(identity=identity,fresh=False)
7     return jsonify(access_token=access_token)
 
        

locations:

  描述:該參數是4.0新增的,指的是用戶攜帶授權token訪問時,其jwt的所處位置列表。例如['headers', 'cookies'],默認為None。什么意思呢?簡單的的說你的token是在headers里呢還是cookies,去訪問的。當然也可以使用JWT_TOKEN_LOCATION,進行全局配置。

6.定制token過期返回信息

1 @jwt.expired_token_loader
2 def expired_token_callback(jwt_header, jwt_payload):
3     return jsonify(msg='token過期了喲'),201

注意的是,以上兩個參數是必填的,第一個時包含jwt的header,第二個是包含jwt的payload(載荷)

 

其實類似功能的裝飾器還有很多,這里不一一描述,更多信息請關注官方文檔,如有錯誤,也請指教!!

待續......

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM