《Python實(shí)現(xiàn)JWT(JSON Web Token)認(rèn)證》要點(diǎn):
本文介紹了Python實(shí)現(xiàn)JWT(JSON Web Token)認(rèn)證,希望對(duì)您有用。如果有疑問(wèn),可以聯(lián)系我們。
作者介紹
張龍(zero),一線運(yùn)維老鳥(niǎo).致力于LINUX/PYTHON/開(kāi)源技術(shù)研究.
首先要明白,認(rèn)證和授權(quán)是不同的.認(rèn)證是判定用戶的合法性,授權(quán)是判定用戶的權(quán)限級(jí)別是否可執(zhí)行后續(xù)操作.這里所講的僅含認(rèn)證.
這是http協(xié)議中所帶帶基本認(rèn)證,是一種簡(jiǎn)單為上的認(rèn)證方式.原理是在每個(gè)請(qǐng)求的header中添加用戶名和密碼的字符串(格式為“username:password”,用base64編碼).這種方式相當(dāng)于將“用戶名:密碼”綁定為一個(gè)開(kāi)放式證書(shū),這會(huì)有幾個(gè)問(wèn)題:
1.每次請(qǐng)求都需要用戶名密碼,如果此連接未使用SSL/TLS,或加密被破解,用戶名密碼基本就暴露了
2.無(wú)法注銷用戶的登錄狀態(tài)
3.證書(shū)不會(huì)過(guò)期,除非修改密碼.
將認(rèn)證的結(jié)果存在客戶端的cookie中,通過(guò)檢查cookie中的身份信息來(lái)作為認(rèn)證結(jié)果.這種方式的特點(diǎn)是便捷,且只需要一次認(rèn)證,多次可用;也可以注銷登錄狀態(tài)和設(shè)置過(guò)期時(shí)間;甚至也有辦法(比如設(shè)置httpOnly)來(lái)避免XSS攻擊.但它的缺點(diǎn)十分明顯,使用cookie那便是有狀態(tài)的服務(wù)了.
JWT協(xié)議似乎已經(jīng)應(yīng)用十分廣泛,JSON Web Token——一種基于token的json格式web認(rèn)證方法.基本的原理是,第一次認(rèn)證通過(guò)用戶名密碼,服務(wù)端簽發(fā)一個(gè)json格式的token.后續(xù)客戶端的請(qǐng)求都攜帶這個(gè)token,服務(wù)端僅需要解析這個(gè)token,來(lái)判別客戶端的身份和合法性.而JWT協(xié)議僅僅規(guī)定了這個(gè)協(xié)議的格式(RFC7519),它的序列生成方法在JWS協(xié)議中描述(https://tools.ietf.org/html/rfc7515),分為三個(gè)部分:
header頭部主要用于聲明類型,這里是jwt,聲明加密的算法 通常直接使用 HMAC SHA256.一種常見(jiàn)的頭部是這樣的:
{ 'typ': 'JWT', 'alg': 'HS256' }
payload是放置實(shí)際有效使用信息的地方.JWT定義了幾種內(nèi)容,包括:標(biāo)準(zhǔn)中注冊(cè)的聲明,如簽發(fā)者,接收者,有效時(shí)間(exp),時(shí)間戳(iat,issued at)等;為官方建議但非必須公共聲明私有聲明 ????????一個(gè)常見(jiàn)的payload是這樣的:
{'user_id': 123456, 'user_role': admin, 'iat': 1467255177 }
事實(shí)上,payload中的內(nèi)容是自由的,按照自己開(kāi)發(fā)的需要加入.?
有個(gè)小問(wèn)題.使用itsdangerous包的TimedJSONWebSignatureSerializer進(jìn)行token序列生成的結(jié)果,exp是在頭部里的.這里似乎違背了jwt的協(xié)議規(guī)則.
這里使用python模塊itsdangerous,這個(gè)模塊能做很多編碼工作,其中一個(gè)是實(shí)現(xiàn)JWS的token序列. ????????genTokenSeq這個(gè)函數(shù)用于生成token.其中使用的是TimedJSONWebSignatureSerializer進(jìn)行序列的生成,這里secretkey密鑰、salt鹽值從配置文件中讀取,當(dāng)然也可以直接寫(xiě)死在這里.expiresin是超時(shí)時(shí)間間隔,這個(gè)間隔以秒記,可以直接在這里設(shè)置,選擇將其設(shè)為方法的形參.
# 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的問(wèn)題.我們只需要用s.dumps將payload的內(nèi)容寫(xiě)進(jìn)來(lái).這里準(zhǔn)備在每個(gè)token中寫(xiě)入三個(gè)值:用戶id、用戶角色id和當(dāng)前時(shí)間(‘iat’是JWT標(biāo)準(zhǔn)注冊(cè)聲明中的一項(xiàng)).
解析需要使用到同樣的serializer,配置一樣的secret key和salt,使用loads方法來(lái)解析token.itsdangerous提供了各種異常處理類,用起來(lái)也很方便.
如果是SignatureExpired,則可以直接返回過(guò)期;
如果是BadSignature,則代表了所有其他簽名錯(cuò)誤的情況,于是又分為:?
# 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]
原文來(lái)自微信公眾號(hào):DevOps視角
轉(zhuǎn)載請(qǐng)注明本頁(yè)網(wǎng)址:
http://www.snjht.com/jiaocheng/4252.html