tips:
由于公司項目都是前后端分離,需要處理用戶認證方面的問題,以及方便應用的擴展。便采用了JWT的方式。
JWT一共由三部分組成,header(頭部)、payload(載荷)、signature(簽名)。
{ "type":"JWT", "alg":"HS256"}
{ "iss":"qin", "exp":1557715124, "user_name":"zhang"}
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1NTc3MjI1NTgsImlzcyI6InFpbiIsInVzZXJfbmFtZSI6InpoYW5nIn0.YHNkSdAMEUIY__U5f9e1tQFAdqiHv_ai_gfaPpPnWLc
pip install PyJWT
util.py
)from datetime import datetime, timedeltaimport jwtkey = "zkpfw*%$qjrfono@sdko34@%"def generate_access_token(user_name: str = "", algorithm: str = 'HS256', exp: float = 2): """ 生成access_token :param user_name: 自定義部分 :param algorithm:加密算法 :param exp:過期時間 :return:token """ now = datetime.utcnow() exp_datetime = now + timedelta(hours=exp) access_payload = { 'exp': exp_datetime, 'flag': 0, # 標識是否為一次性token,0是,1不是 'iat': now, # 開始時間 'iss': 'qin', # 簽名 'user_name': user_name # 自定義部分 } access_token = jwt.encode(access_payload, key, algorithm=algorithm) return access_tokendef generate_refresh_token(user_name: str = "", algorithm: str = 'HS256', fresh: float = 30): """ 生成refresh_token :param user_name: 自定義部分 :param algorithm:加密算法 :param fresh:過期時間 :return:token """ now = datetime.utcnow() # 刷新時間為30天 exp_datetime = now + timedelta(days=fresh) refresh_payload = { 'exp': exp_datetime, 'flag': 1, # 標識是否為一次性token,0是,1不是 'iat': now, # 開始時間 'iss': 'qin', # 簽名, 'user_name': user_name # 自定義部分 } refresh_token = jwt.encode(refresh_payload, key, algorithm=algorithm) return refresh_tokendef decode_auth_token(token: str): """ 解密token :param token:token字符串 :return: """ try: # 取消過期時間驗證 # payload = jwt.decode(token, key, options={'verify_exp': False}) payload = jwt.decode(token, key=key, ) except (jwt.ExpiredSignatureError, jwt.InvalidTokenError, jwt.InvalidSignatureError): return "" else: return payloaddef identify(auth_header: str): """ 用戶鑒權 :return: """ if auth_header: payload = decode_auth_token(auth_header) if not payload: return False if "user_name" in payload and "flag" in payload: if payload["flag"] == 1: # 用來獲取新access_token的refresh_token無法獲取數(shù)據(jù) return False elif payload["flag"] == 0: return payload["user_name"] else: # 其他狀態(tài)暫不允許 return False else: return False else: return False
util.py
)from functools import wrapsdef login_required(f): """ 登陸保護,驗證用戶是否登陸 :param f: :return: """ @wraps(f) def wrapper(*args, **kwargs): token = request.headers.get("Authorization", default=None) if not token: return "請登陸" user_name = identify(token) if not user_name: return "請登陸" # 獲取到用戶并寫入到session中,方便后續(xù)使用 session["user_name"] = user_name return f(*args, **kwargs) return wrapper
from flask import Flaskfrom util import *app = Flask(__name__)app.config["SECRET_KEY"] = "reqweqwcasd!#$%456421&^%&^%"@app.route('/testLogin', methods=["POST"])def test_login(): """ 登陸成功獲取到數(shù)據(jù)獲取token和刷新token :return: """ obj = request.get_json(force=True) name = obj.get("name") if not obj or not name: return "參數(shù)錯誤" if name == "qin": access_token = generate_access_token(user_name=name) refresh_token = generate_refresh_token(user_name=name) data = {"access_token": access_token.decode("utf-8"), "refresh_token": refresh_token.decode("utf-8")} return jsonify(data) else: return "用戶名或密碼錯誤"@app.route('/testGetData', methods=["GET"])@login_requireddef test_get_data(): """ 測試登陸保護下獲取數(shù)據(jù) :return: """ name = session.get("user_name") return "{},你好??!".format(name)@app.route('/testRefreshToken', methods=["GET"])def test_refresh_token(): """ 刷新token,獲取新的數(shù)據(jù)獲取token :return: """ refresh_token = request.args.get("refresh_token") if not refresh_token: return "參數(shù)錯誤" payload = decode_auth_token(refresh_token) if not payload: return "請登陸" if "user_name" not in payload: return "請登陸" access_token = generate_access_token(user_name=payload["user_name"]) data = {"access_token": access_token.decode("utf-8"), "refresh_token": refresh_token} return jsonify(data)if __name__ == '__main__': app.run()
請求鏈接:http://127.0.0.1:5000/testLogin請求方式:POST請求數(shù)據(jù):{"name":"qin"}數(shù)據(jù)方式:json服務端響應:{"access_token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1NTc3MzM5OTUsImZsYWciOjAsImlhdCI6MTU1NzcyNjc5NSwiaXNzIjoicWluIiwidXNlcl9uYW1lIjoicWluIn0.PBWk8LOB_S4TVRg7BrXQ9vGjjM31veqgkgbyinVdlVc","refresh_token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1NjAzMTg3OTUsImZsYWciOjEsImlhdCI6MTU1NzcyNjc5NSwiaXNzIjoicWluIiwidXNlcl9uYW1lIjoicWluIn0.kn0-TkP79XlUbCZDeCX7R6oFvG9-M1kYER_7P_d0dTM"}
請求鏈接:http://127.0.0.1:5000/testGetData請求方式:GET請求頭:Authorization = eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1NTc3MzM5OTUsImZsYWciOjAsImlhdCI6MTU1NzcyNjc5NSwiaXNzIjoicWluIiwidXNlcl9uYW1lIjoicWluIn0.PBWk8LOB_S4TVRg7BrXQ9vGjjM31veqgkgbyinVdlVc服務端響應:qin,你好??!
請求鏈接:http://127.0.0.1:5000/testRefreshToken請求方式:GET請求參數(shù):refresh_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1NjAzMTg3OTUsImZsYWciOjEsImlhdCI6MTU1NzcyNjc5NSwiaXNzIjoicWluIiwidXNlcl9uYW1lIjoicWluIn0.kn0-TkP79XlUbCZDeCX7R6oFvG9-M1kYER_7P_d0dTM服務端響應:{"access_token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1NTc3MzQwNjksImZsYWciOjAsImlhdCI6MTU1NzcyNjg2OSwiaXNzIjoicWluIiwidXNlcl9uYW1lIjoicWluIn0.z-DLcBRRh6pE_wQqfF_YjQMxupbGVI2KD-v9jzEz-H0","refresh_token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1NjAzMTg3OTUsImZsYWciOjEsImlhdCI6MTU1NzcyNjc5NSwiaXNzIjoicWluIiwidXNlcl9uYW1lIjoicWluIn0.kn0-TkP79XlUbCZDeCX7R6oFvG9-M1kYER_7P_d0dTM"}
前文只是簡單現(xiàn)實了flask和jwt的結合,實際中使用也會有一定的問題。
一些解決思路: