日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

肝!一文讲解JWT用户认证全过程

發布時間:2024/9/15 编程问答 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 肝!一文讲解JWT用户认证全过程 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.


什么是JWT(what)

  • JWT(JSON Web Token)是一個開放標準(RFC 7519),它定義了一種緊湊且自包含的方式,以JSON對象的形式在各方之間安全地傳輸信息。

  • JWT是一個數字簽名,生成的信息是可以驗證并被信任的。

  • 使用密鑰(使用HMAC算法)或使用RSA或ECDSA的公鑰/私鑰對JWT進行簽名。

  • JWT是目前最流行的跨域認證解決方案

JWT令牌結構

SON Web令牌以緊湊的形式由三部分組成,這些部分由點(.)分隔,分別是:

  • Header

  • Payload

  • Signature

即為:xxxx.yyyy.zzzz

Header

Header通常由兩部分組成:令牌的類型(即JWT)和所使用的簽名算法(例如HMAC SHA256或RSA)。例如:

{"alg": "HS256","typ": "JWT" }

Header會被Base64Url編碼為JWT的第一部分。即為:

$ echo -n '{"alg":"HS256","typ":"JWT"}'|base64 eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9

Payload

Payload是有關實體(通常是用戶)和其他數據的聲明,它包含三部分:

注冊聲明

這些是一組預定義的權利要求,不是強制性的,而是建議使用的,以提供一組有用的可互操作的權利要求。其中一些是:iss(JWT的簽發者), exp(expires,到期時間), sub(主題), aud(JWT接收者),iat(issued at,簽發時間)等。

注意:聲明名稱都是三個字符

公開聲明

公共的聲明可以添加任何的信息,一般添加用戶的相關信息或其他業務需要的必要信息.但不建議添加敏感信息,因為該部分在客戶端可解密。

私有聲明

私有聲明是提供者和消費者所共同定義的聲明,一般不建議存放敏感信息,因為base64是對稱解密的,意味著該部分信息可以歸類為明文信息。

例子:

{ "iat": 1593955943, "exp": 1593955973, "uid": 10, "username": "test", "scopes": [ "admin", "user" ] }

Payload會被Base64Url編碼為JWT的第二部分。即為:

$ echo -n '{"iat":1593955943,"exp":1593955973,"uid":10,"username":"test","scopes":["admin","user"]}'|base64 eyJ1c2VybmFtZSI6InRlc3QiLCJpYXQiOjE1OTM5NTU5NDMsInVpZCI6MTAsImV4cCI6MTU5Mzk1NTk3Mywic2NvcGVzIjpbImFkbWluIiwidXNlciJdfQ

注意:對于已簽名的令牌,此信息盡管可以防止篡改,但任何人都可以讀取。除非將其加密,否則請勿將機密信息放入JWT的有效負載或報頭元素中。

Signature

Signature部分的生成需要base64編碼之后的Header,base64編碼之后的Payload,密鑰(secret),Header需要指定簽字的算法。

例如,如果要使用HMAC SHA256算法,則將通過以下方式創建簽名:

HMACSHA256(base64UrlEncode(header) + "." +base64UrlEncode(payload),secret)

整合在一起

輸出是三個由點分隔的Base64-URL字符串,可以在HTML和HTTP環境中輕松傳遞這些字符串,與基于XML的標準(例如SAML)相比,它更緊湊。

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InRlc3QiLCJpYXQiOjE1OTM5NTU5NDMsInVpZCI6MTAsImV4cCI6MTU5Mzk1NTk3Mywic2NvcGVzIjpbImFkbWluIiwidXNlciJdfQ.VHpxmxKVKpsn2Iytqc_6Z1U1NtiX3EgVki4PmA-J3Pg

JWT是無狀態授權機制,服務器的受保護路由將Header中檢查有效的token,如果存在,則將允許用戶訪問受保護的資源。如果JWT包含必要的數據,則可以減少查詢數據庫中某些操作的需求。

什么時候使用JWT(when)

  • 授權:一旦用戶登錄,每個后續請求將包括JWT,從而允許用戶訪問該令牌允許的路由,服務和資源。單一登錄是當今廣泛使用JWT的一項功能,因為它的開銷很小并且可以在不同的域中輕松使用。這也是JWT最常見的方案。

  • 信息交換:JSON Web令牌是各方之間安全地傳輸信息的好辦法。對JWT進行簽名,所以您可以確保發件人是他們所說的人。由于,簽名可以設置有效時長,可以驗證內容是否遭到篡改。

如何使用JWT(how)

JWT工作流程

根據下面的這張流程圖來分析一下JWT的工作過程

  • 1 用戶登錄:提供用戶名和密碼;

  • 2 JWT生成token和refreshtoken,返回客戶端;(注意:refreshtoken的過期時間長于token的過期時間

  • 3 客戶端保存token和refresh_token,并攜帶token,請求服務端資源;

  • 4 服務端判斷token是否過期,若沒有過期,則解析token獲取認證相關信息,認證通過后,將服務器資源返回給客戶端;

  • 5 服務端判斷token是否過期,若token已過期,返回token過期提示;

  • 6 客戶端獲取token過期提示后,用refresh_token接著繼續上一次請求;

  • 7 服務端判斷refreshtoken是否過期,若沒有過期,則生成新的token和refreshtoken,并返回給客戶端,客戶端丟棄舊的token,保存新的token;

  • 8 服務端判斷refreshtoken是否過期,若refreshtoken已過期,則返回給客戶端token過期,需要重新登錄的提示。

python+flask+JWT實戰

import timefrom functools import wraps from flask import Flask, request, jsonify import jwt from jwt import ExpiredSignatureErrorapp = Flask(__name__)max_time = 60 refresh_max_time = 120 token_secret = "This is a secret"def verify_token(func):@wraps(func)def decorator(*args, **kwargs):try:token = request.headers["token"]print(token)data = jwt.decode(token, token_secret, algorithms=['HS256'])now = int(time.time())time_interval = now - data['time']if time_interval >= max_time:# create new tokentoken, refresh_token = creat_token()return jsonify({"token": token, "refresh_token": refresh_token})except ExpiredSignatureError:return "Token expired"except Exception as ex:print(ex)return "Log in again"return func(*args, **kwargs)return decoratordef creat_token(uid):now = int(time.time())payload = {'uid': uid, 'time': now, 'exp': now + max_time}refresh_payload = {'uid': uid, 'time': now, 'exp': now + refresh_max_time}token = jwt.encode(payload, token_secret, algorithm='HS256')refresh_token = jwt.encode(refresh_payload, token_secret, algorithm='HS256')return token, refresh_token@app.route('/login', methods=["POST"]) def login():user_name = request.values.get('user_name')password = request.values.get('password')# @TODO 根據user_name和password 獲取唯一的uiduid = 10token, refresh_token = creat_token(uid=uid)return jsonify({"token": token, "refresh_token": refresh_token})@app.route('/test', methods=['GET']) @verify_token def test():return 'hello world'if __name__ == "__main__":app.run(host="0.0.0.0", port=5000)

第三方庫-itsdangerous

isdangerous簡介

itsdangerous支持JSON Web 簽名 (JWS),內部默認使用了HMAC和SHA1來簽名,其中類 JSONWebSignatureSerializer內部與JWT一致,也分成三部分(header,payload,signature),查看源碼可知:

def dumps(self, obj, salt=None, header_fields=None):"""Like :meth:`.Serializer.dumps` but creates a JSON WebSignature. It also allows for specifying additional fields to beincluded in the JWS header."""header = self.make_header(header_fields)signer = self.make_signer(salt, self.algorithm)return signer.sign(self.dump_payload(header, obj)) def dump_payload(self, header, obj):base64d_header = base64_encode(self.serializer.dumps(header, **self.serializer_kwargs))base64d_payload = base64_encode(self.serializer.dumps(obj, **self.serializer_kwargs))return base64d_header + b"." + base64d_payload
  • obj保存用戶相關信息,類似JWT中的payload

  • base64url對obj和header進行編碼之后,使用?.拼接

  • 將拼接之后的數據,作為signer的輸入以及初始化?__init__中用戶定義的secret來生成新的token

感興趣的朋友可以直接參看github源碼,這里不再展開贅述。

python+flask+isdangerous實戰

import?timefrom functools import wraps from flask import Flask, request, jsonify from itsdangerous import TimedJSONWebSignatureSerializer as Serializer, SignatureExpiredapp = Flask(__name__)max_time = 60 refresh_max_time = 120 token_secret = "This is a secret"def verify_token(func):@wraps(func)def decorator(*args, **kwargs):try:token = request.headers["token"]print(token)s = Serializer(token_secret)data = s.loads(token)now = int(time.time())time_interval = now - data['time']if time_interval >= max_time:# create new tokentoken, refresh_token = creat_token()return jsonify({"token": token, "refresh_token": refresh_token})except SignatureExpired:return "Token expired"except Exception as ex:print(ex)return "Log in again"return func(*args, **kwargs)return decoratordef creat_token(uid):now = int(time.time())s = Serializer(token_secret, expires_in=max_time)token = s.dumps({"uid": uid, "time": now}).decode("ascii")refresh_s = Serializer(token_secret, expires_in=refresh_max_time)refresh_token = refresh_s.dumps({"uid": uid, "time": now}).decode("ascii")return token, refresh_token@app.route('/token', methods=["POST"]) def token():user_name = request.values.get('user_name')password = request.values.get('password')# @TODO 根據user_name和password 獲取唯一的uiduid = 10token, refresh_token = creat_token(uid=uid)return jsonify({"token": token, "refresh_token": refresh_token})@app.route('/test', methods=['GET']) @verify_token def test():return 'hello world'if __name__ == "__main__":app.run(host="0.0.0.0")

TimedJSONWebSignatureSerializer相比 JSONWebSignatureSerializer在header中贈加了過期時間,如果過期會拋出 SignatureExpired異常。

問題

用戶登出,如何設置token無效?

JWT是無狀態的,用戶登出設置token無效就已經違背了JWT的設計原則,但是在實際應用場景中,這種功能是需要的,那該如何實現呢?提供幾種思路:

  • 用戶登出,瀏覽器端丟棄token

  • 使用redis數據庫,用戶登出,從redis中刪除對應的token,請求訪問時,需要從redis庫中取出對應的token,若沒有,則表明已經登出

使用redis,兩個不同的設備,一個設備登出,另外一個設備如何處理?

請思考這樣一種場景:

  • 同一個用戶從兩個設備登陸到服務端(設備1,設備2);

  • 設備1登出,刪除redis中的對應的token

  • 設備2再次請求數據時,redis中的數據為空,需要重新登錄。

很明顯,這種情況是不應該出現的,說一下自己的想法:

  • 每一個設備與用戶生成唯一的key,保存在redis中,即設備1的用戶登出,只刪除對應的token,設備2的token仍然存在

  • 服務器端維護一個版本號,相同用戶不同設備登入,版本號加1,這樣保持key的唯一性(和上面差不多)

?往期推薦?

????

  • 實戰|利用機器學習解決一個多分類任務

  • 某程序員動了公司的祖傳代碼“屎山”,半年后怒交辭職報告!

  • 告別刷抖音!30秒一個Python小例子,總有一款適合你

  • 這個只有1.5M的軟件,能讓你的網速快3倍

  • - End -

    最后說一個題外話,相信大家有不少人開通了視頻號。小詹也開通了一個視頻號,會分享互聯網那些事、讀書心得與副業經驗,歡迎各位掃描下方二維碼關注。

    總結

    以上是生活随笔為你收集整理的肝!一文讲解JWT用户认证全过程的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。