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(載荷)
其實類似功能的裝飾器還有很多,這里不一一描述,更多資訊請關注官方檔案,如有錯誤,也請指教!!
待續......
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/289115.html
標籤:Python
上一篇:Python基礎之資料可視化
