簡單介紹一下jwt
Json Web Token(JWT)
JWT 是一個開放標准(RFC 7519),它定義了一種用於簡潔,自包含的用於通信雙方之間以 JSON 對象的形式安全傳遞信息的方法。JWT 可以使用 HMAC 算法或者是 RSA 的公鑰密鑰對進行簽名。它具備兩個特點:
簡潔(Compact)
可以通過URL, POST 參數或者在 HTTP header 發送,因為數據量小,傳輸速度快
自包含(Self-contained)
負載中包含了所有用戶所需要的信息,避免了多次查詢數據庫
詳情請查看這篇文章
https://www.jianshu.com/p/180a870a308a
或者:點我
JWT長什么樣?
JWT是由三段信息構成的,將這三段信息文本用.
鏈接一起就構成了Jwt字符串。就像這樣:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
JWT的構成
第一部分我們稱它為頭部(header),第二部分我們稱其為載荷(payload, 類似於飛機上承載的物品),第三部分是簽證(signature).
header
jwt的頭部承載兩部分信息:
- 聲明類型,這里是jwt
- 聲明加密的算法 通常直接使用 HMAC SHA256
完整的頭部就像下面這樣的JSON:
{ 'typ': 'JWT', 'alg': 'HS256' }
然后將頭部進行base64加密(該加密是可以對稱解密的),構成了第一部分.
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
playload
載荷就是存放有效信息的地方。這個名字像是特指飛機上承載的貨品,這些有效信息包含三個部分
- 標准中注冊的聲明
- 公共的聲明
- 私有的聲明
標准中注冊的聲明 (建議但不強制使用) :
- iss: jwt簽發者
- sub: jwt所面向的用戶
- aud: 接收jwt的一方
- exp: jwt的過期時間,這個過期時間必須要大於簽發時間
- nbf: 定義在什么時間之前,該jwt都是不可用的.
- iat: jwt的簽發時間
- jti: jwt的唯一身份標識,主要用來作為一次性token,從而回避重放攻擊。
公共的聲明 :
公共的聲明可以添加任何的信息,一般添加用戶的相關信息或其他業務需要的必要信息.但不建議添加敏感信息,因為該部分在客戶端可解密.
私有的聲明 :
私有聲明是提供者和消費者所共同定義的聲明,一般不建議存放敏感信息,因為base64是對稱解密的,意味着該部分信息可以歸類為明文信息。
定義一個payload:
{ "sub": "1234567890", "name": "John Doe", "admin": true }
然后將其進行base64加密,得到Jwt的第二部分。
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9
signature
jwt的第三部分是一個簽證信息,這個簽證信息由三部分組成:
- header (base64后的)
- payload (base64后的)
- secret
這個部分需要base64加密后的header和base64加密后的payload使用.
連接組成的字符串,然后通過header中聲明的加密方式進行加鹽secret
組合加密,然后就構成了jwt的第三部分。
// javascript var encodedString = base64UrlEncode(header) + '.' + base64UrlEncode(payload); var signature = HMACSHA256(encodedString, 'secret'); // TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
將這三部分用.
連接成一個完整的字符串,構成了最終的jwt:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
注意:secret是保存在服務器端的,jwt的簽發生成也是在服務器端的,secret就是用來進行jwt的簽發和jwt的驗證,所以,它就是你服務端的私鑰,在任何場景都不應該流露出去。一旦客戶端得知這個secret, 那就意味着客戶端是可以自我簽發jwt了。
如何應用
一般是在請求頭里加入Authorization
,並加上Bearer
標注:
fetch('api/user/1', { headers: { 'Authorization': 'Bearer ' + token } })
服務端會驗證token,如果驗證通過就會返回相應的資源。整個流程就是這樣的:

總結
優點
- 因為json的通用性,所以JWT是可以進行跨語言支持的,像JAVA,JavaScript,NodeJS,PHP等很多語言都可以使用。
- 因為有了payload部分,所以JWT可以在自身存儲一些其他業務邏輯所必要的非敏感信息。
- 便於傳輸,jwt的構成非常簡單,字節占用很小,所以它是非常便於傳輸的。
- 它不需要在服務端保存會話信息, 所以它易於應用的擴展
安全相關
- 不應該在jwt的payload部分存放敏感信息,因為該部分是客戶端可解密的部分。
- 保護好secret私鑰,該私鑰非常重要。
- 如果可以,請使用https協議
使用
---------------------
Django REST framework JWT 的使用
安裝
pip install djangorestframework-jwt
配置
SECRET_KEY = '8bu4e=em53a(p904(#^0)f0hx@gz)!8_(2h@jih@*&6dy-2*c*' #用於jwt簽發和驗證,這個千萬不能泄漏,否則客戶端也可以隨意簽發jwt,有安全風險
REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': ( 'rest_framework_jwt.authentication.JSONWebTokenAuthentication', 'rest_framework.authentication.SessionAuthentication', 'rest_framework.authentication.BasicAuthentication', ), } JWT_AUTH = { 'JWT_EXPIRATION_DELTA': datetime.timedelta(days=1), }
注意:secret_key是保存在服務器端的,jwt的簽發生成也是在服務器端的,secret_key就是用來進行jwt的簽發和jwt的驗證,所以,它就是你服務端的私鑰,在任何場景都不應該流露出去。一旦客戶端得知這個secret, 那就意味着客戶端是可以自我簽發jwt了。
JWT_EXPIRATION_DELTA 指明token的有效期
url.py
from django.contrib import admin from django.urls import path,include from rest_framework_jwt.views import obtain_jwt_token, verify_jwt_token, refresh_jwt_token from accounts.views import xd_obtain_jwt_token from common.views import page_not_found urlpatterns = [ path('admin/', admin.site.urls), path('login', obtain_jwt_token, name='login'),#使用默認自帶的
path('login', xd_obtain_jwt_token, name='login'),#或者這是使用自定義的
]
自定義的時候的寫法
from rest_framework_jwt.views import JSONWebTokenAPIView from rest_framework_jwt.utils import jwt_response_payload_handler from rest_framework_jwt.serializers import JSONWebTokenSerializer class XD_JSONWebTokenAPIView(JSONWebTokenAPIView): """ Base API View that various JWT interactions inherit from. """ def post(self, request, *args, **kwargs): serializer = self.get_serializer(data=request.data) if serializer.is_valid(): user = serializer.object.get('user') or request.user token = serializer.object.get('token') response_data = jwt_response_payload_handler(token, user, request) response_data['is_first']=user.is_default_pwd response = Response(response_data) print(response,'這是response===',response_data,user,type(user)) if api_settings.JWT_AUTH_COOKIE: print('是否進來了') expiration = (datetime.utcnow() + api_settings.JWT_EXPIRATION_DELTA) response.set_cookie(api_settings.JWT_AUTH_COOKIE, token, expires=expiration, httponly=True) return response return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) class ObtainJSONWebToken(XD_JSONWebTokenAPIView): """ API View that receives a POST with a user's username and password. Returns a JSON Web Token that can be used for authenticated requests. """ serializer_class = JSONWebTokenSerializer xd_obtain_jwt_token = ObtainJSONWebToken.as_view()
JSONWebTokenSerializer做的工作:
1.
登陸數據用戶名密碼前段得到的返回值:
{ "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxLCJ1c2VybmFtZSI6IjE4NzAxNTEzNDQwIiwiZXhwIjoxNTQ0ODQ1MjgzLCJwaG9uZSI6IjE4NzAxNTEzNDQwIn0.Gt7VUIFbW3wski9c53nw69IiIP-5l_0xKNpPimz2BJg", "is_first": true }