《Python實現JWT(JSON Web Token)認證》要點:
本文介紹了Python實現JWT(JSON Web Token)認證,希望對您有用。如果有疑問,可以聯系我們。
作者介紹
張龍(zero),一線運維老鳥.致力于LINUX/PYTHON/開源技術研究.
首先要明白,認證和授權是不同的.認證是判定用戶的合法性,授權是判定用戶的權限級別是否可執行后續操作.這里所講的僅含認證.
這是http協議中所帶帶基本認證,是一種簡單為上的認證方式.原理是在每個請求的header中添加用戶名和密碼的字符串(格式為“username:password”,用base64編碼).這種方式相當于將“用戶名:密碼”綁定為一個開放式證書,這會有幾個問題:
1.每次請求都需要用戶名密碼,如果此連接未使用SSL/TLS,或加密被破解,用戶名密碼基本就暴露了
2.無法注銷用戶的登錄狀態
3.證書不會過期,除非修改密碼.
將認證的結果存在客戶端的cookie中,通過檢查cookie中的身份信息來作為認證結果.這種方式的特點是便捷,且只需要一次認證,多次可用;也可以注銷登錄狀態和設置過期時間;甚至也有辦法(比如設置httpOnly)來避免XSS攻擊.但它的缺點十分明顯,使用cookie那便是有狀態的服務了.
JWT協議似乎已經應用十分廣泛,JSON Web Token——一種基于token的json格式web認證方法.基本的原理是,第一次認證通過用戶名密碼,服務端簽發一個json格式的token.后續客戶端的請求都攜帶這個token,服務端僅需要解析這個token,來判別客戶端的身份和合法性.而JWT協議僅僅規定了這個協議的格式(RFC7519),它的序列生成方法在JWS協議中描述(https://tools.ietf.org/html/rfc7515),分為三個部分:
header頭部主要用于聲明類型,這里是jwt,聲明加密的算法 通常直接使用 HMAC SHA256.一種常見的頭部是這樣的:
{ 'typ': 'JWT', 'alg': 'HS256' }
payload是放置實際有效使用信息的地方.JWT定義了幾種內容,包括:標準中注冊的聲明,如簽發者,接收者,有效時間(exp),時間戳(iat,issued at)等;為官方建議但非必須公共聲明私有聲明 ????????一個常見的payload是這樣的:
{'user_id': 123456, 'user_role': admin, 'iat': 1467255177 }
事實上,payload中的內容是自由的,按照自己開發的需要加入.?
有個小問題.使用itsdangerous包的TimedJSONWebSignatureSerializer進行token序列生成的結果,exp是在頭部里的.這里似乎違背了jwt的協議規則.
這里使用python模塊itsdangerous,這個模塊能做很多編碼工作,其中一個是實現JWS的token序列. ????????genTokenSeq這個函數用于生成token.其中使用的是TimedJSONWebSignatureSerializer進行序列的生成,這里secretkey密鑰、salt鹽值從配置文件中讀取,當然也可以直接寫死在這里.expiresin是超時時間間隔,這個間隔以秒記,可以直接在這里設置,選擇將其設為方法的形參.
# serializer for JWT from itsdangerous import TimedJSONWebSignatureSerializer as Serializer def genTokenSeq(self, expires): s = Serializer( secret_key=app.config['SECRET_KEY'], salt=app.config['AUTH_SALT'], expires_in=expires) timestamp = time.time() return s.dumps( {'user_id': self.user_id, 'user_role': self.role_id, 'iat': timestamp})
使用Serializer可以幫我們處理好header、signature的問題.我們只需要用s.dumps將payload的內容寫進來.這里準備在每個token中寫入三個值:用戶id、用戶角色id和當前時間(‘iat’是JWT標準注冊聲明中的一項).
解析需要使用到同樣的serializer,配置一樣的secret key和salt,使用loads方法來解析token.itsdangerous提供了各種異常處理類,用起來也很方便.
如果是SignatureExpired,則可以直接返回過期;
如果是BadSignature,則代表了所有其他簽名錯誤的情況,于是又分為:?
# serializer for JWT from itsdangerous import TimedJSONWebSignatureSerializer as Serializer # exceptions for JWT from itsdangerous import SignatureExpired, BadSignature, BadData # Class xxx def tokenAuth(token): s = Serializer( secret_key=api.app.config['SECRET_KEY'], salt=api.app.config['AUTH_SALT']) try: data = s.loads(token) except SignatureExpired: msg = 'token expired' app.logger.warning(msg) return [None, None, msg] except BadSignature, e: encoded_payload = e.payload if encoded_payload is not None: try: s.load_payload(encoded_payload) except BadData: msg = 'token tampered' app.logger.warning(msg) return [None, None, msg] msg = 'badSignature of token' app.logger.warning(msg) return [None, None, msg] except: msg = 'wrong token with unknown reason' app.logger.warning(msg) return [None, None, msg] if ('user_id' not in data) or ('user_role' not in data): msg = 'illegal payload inside' app.logger.warning(msg) return [None, None, msg] msg = 'user(' + data['user_id'] + ') logged in by token.' userId = data['user_id'] roleId = data['user_role'] return [userId, roleId, msg]
原文來自微信公眾號:DevOps視角