溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

django drf框架怎么實現user驗證以及JWT的構成

發布時間:2021-09-06 10:05:29 來源:億速云 閱讀:153 作者:chen 欄目:開發技術

本篇內容介紹了“django drf框架怎么實現user驗證以及JWT的構成”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!

登錄注冊是幾乎所有網站都需要去做的接口,而說到登錄,自然也就涉及到驗證以及用戶登錄狀態保存,最近用DRF在做的一個關于網上商城的項目中,引入了一個拓展DRF JWT,專門用于做驗證和用戶狀態保存。這個拓展比傳統的CSRF更加安全。先來介紹一下JWT認證機制吧!

Json web token (JWT), 是為了在網絡應用環境間傳遞聲明而執行的一種基于JSON的開放標準( (RFC 7519 ).該token被設計為緊湊且安全的,特別適用于分布式站點的單點登錄(SSO)場景。JWT的聲明一般被用來在身份提供者和服務提供者間傳遞被認證的用戶身份信息,以便于從資源服務器獲取資源,也可以增加一些額外的其它業務邏輯所必須的聲明信息,該token也可直接被用于認證,也可被加密。

基于token的鑒權機制

基于token的鑒權機制類似于http協議也是無狀態的,它不需要在服務端去保留用戶的認證信息或者會話信息。這就意味著基于token認證機制的應用不需要去考慮用戶在哪一臺服務器登錄了,這就為應用的擴展提供了便利。

流程上是這樣的:

  • 用戶使用用戶名密碼來請求服務器

  • 服務器進行驗證用戶的信息

  • 服務器通過驗證發送給用戶一個token

  • 客戶端存儲token,并在每次請求時附送上這個token值

  • 服務端驗證token值,并返回數據

這個token必須要在每次請求時傳遞給服務端,它應該保存在請求頭里, 另外,服務端要支持 CORS(跨來源資源共享) 策略,一般我們在服務端這么做就可以了 Access-Control-Allow-Origin: * 。

那么我們現在回到JWT的主題上。

JWT的構成

第一部分我們稱它為頭部(header),第二部分我們稱其為載荷(payload, 類似于飛機上承載的物品),第三部分是簽證(signature).

jwt的頭部承載兩部分信息:1,聲明類型,這里是jwt,2聲明加密的算法 通常直接使用 HMAC SHA256。完整的頭部就像下面這樣的JSON:

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

然后將頭部進行base64加密(該加密是可以對稱解密的),構成了第一部分。

如 eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9。

載荷就是存放有效信息的地方。這個名字像是特指飛機上承載的貨品,這些有效信息包含三個部分1標準中注冊的聲明,2公共的聲明,3私有的聲明。標準中注冊的聲明 (建議但不強制使用) :1 iss: jwt簽發者,2 sub: jwt所面向的用戶,3 aud: 接收jwt的一方,4 exp: jwt的過期時間,這個過期時間必須要大于簽發時間,5 nbf: 定義在什么時間之前,該jwt都是不可用的,6 iat: jwt的簽發時間,7 jti: jwt的唯一身份標識,主要用來作為一次性token,從而回避重放攻擊。公共的聲明 : 公共的聲明可以添加任何的信息,一般添加用戶的相關信息或其他業務需要的必要信息.但不建議添加敏感信息,因為該部分在客戶端可解密.私有的聲明 : 私有聲明是提供者和消費者所共同定義的聲明,一般不建議存放敏感信息,因為base64是對稱解密的,意味著該部分信息可以歸類為明文信息。定義一個payload:

{
 "sub": "1234567890",
 "name": "John Doe",
 "admin": true
}

然后將其進行base64加密,得到JWT的第二部分。

如 eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9。

JWT的第三部分是一個簽證信息,這個簽證信息由三部分組成:1 header (base64后的),2 payload (base64后的),3 secret。這個部分需要base64加密后的header和base64加密后的payload使用 . 連接組成的字符串,然后通過header中聲明的加密方式進行加鹽 secret 組合加密,然后就構成了jwt的第三部分。

注意:secret是保存在服務器端的,jwt的簽發生成也是在服務器端的,secret就是用來進行jwt的簽發和jwt的驗證,所以,它就是你服務端的私鑰,在任何場景都不應該流露出去。一旦客戶端得知這個secret, 那就意味著客戶端是可以自我簽發jwt了。

首先需要安裝拓展  pip install djangorestframework-jwt,然后在django進行配置, JWT_EXPIRATION_DELTA 指明token的有效期。

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),
}

Django REST framework JWT 擴展的說明文檔中提供了手動簽發JWT的方法

from rest_framework_jwt.settings import api_settings

jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER

payload = jwt_payload_handler(user)
token = jwt_encode_handler(payload)

在注冊時,引入上述代碼,簽發JWT即可。而對于登錄,JWT拓展提供了內置的視圖,在urls中添加對于路由即可。

from rest_framework_jwt.views import obtain_jwt_token

urlpatterns = [
 url(r'^authorizations/$', obtain_jwt_token),
]

雖然寫起來很簡單,但是內部其實做了很多的事,今天就來詳細研究一下,源代碼內部做了哪些事情。

當用戶登錄,會以post形式發請求到后端,會訪問 obtain_jwt_token中的post方法,在源代碼中可以看到 obtain_jwt_token = ObtainJSONWebToken.as_view(),跟我們寫的類視圖十分類似,這是一個內部已經寫好的類視圖。

class ObtainJSONWebToken(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

該類并未定義任何方法,所以對應的post方法應該寫在父類,下面是父類中的post方法

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 = Response(response_data)
   if api_settings.JWT_AUTH_COOKIE:
    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)

上述方法返回一個Response對象,經過一系列操作返回到前端,訪問結束。

而在DRF框架中,在調用視圖之前,就會進行相應的驗證操作。想要了解整個過程,需要我們從源代碼中一步步去探索。當前端發起一個請求到后端,會根據路由訪問對象的視圖類的as_view()方法,該方法會接著調用dispatch()方法,APIView是DRF中所有視圖類的父類,可以看一下他的dispatch方法。

def dispatch(self, request, *args, **kwargs):
  """
  `.dispatch()` is pretty much the same as Django's regular dispatch,
  but with extra hooks for startup, finalize, and exception handling.
  """
  self.args = args
  self.kwargs = kwargs
  request = self.initialize_request(request, *args, **kwargs)
  self.request = request
  self.headers = self.default_response_headers # deprecate?

  try:
   self.initial(request, *args, **kwargs)

   # Get the appropriate handler method
   if request.method.lower() in self.http_method_names:
    handler = getattr(self, request.method.lower(),
         self.http_method_not_allowed)
   else:
    handler = self.http_method_not_allowed

   response = handler(request, *args, **kwargs)

  except Exception as exc:
   response = self.handle_exception(exc)

  self.response = self.finalize_response(request, response, *args, **kwargs)
  return self.response

可以看到,到請求進來,會調用self.initalize_request()方法對請求進行處理。

def initialize_request(self, request, *args, **kwargs):
  """
  Returns the initial request object.
  """
  parser_context = self.get_parser_context(request)

  return Request(
   request,
   parsers=self.get_parsers(),
   authenticators=self.get_authenticators(),
   negotiator=self.get_content_negotiator(),
   parser_context=parser_context
  )

我們需要關注的只有 authenticators=self.get_authenticators()這一句,

def get_authenticators(self):
  """
  Instantiates and returns the list of authenticators that this view can use.
  """
  return [auth() for auth in self.authentication_classes]

接著往上照,可以看到類屬性 permission_classes = api_settings.DEFAULT_PERMISSION_CLASSES,這就是要什么我們要在django配置中加入DEFAULT_PERMISSION_CLASSES配置,上述方法會遍歷我們在配置中寫到的列表,拿到里面的驗證類,并進行實例化,并將生成的對象裝在一個新的列表中,保存在新生成的Request對象中。然后我們接著看dispatch方法,在實際調用視圖類中的對應方法前,還調用了self.initial()方法進行一些初始化操作。

def initial(self, request, *args, **kwargs):
  """
  Runs anything that needs to occur prior to calling the method handler.
  """
  self.format_kwarg = self.get_format_suffix(**kwargs)

  # Perform content negotiation and store the accepted info on the request
  neg = self.perform_content_negotiation(request)
  request.accepted_renderer, request.accepted_media_type = neg

  # Determine the API version, if versioning is in use.
  version, scheme = self.determine_version(request, *args, **kwargs)
  request.version, request.versioning_scheme = version, scheme

  # Ensure that the incoming request is permitted
  self.perform_authentication(request)
  self.check_permissions(request)
  self.check_throttles(request)

我們需要關注的是最后三排,分別調用了三個方法,進行身份驗證,權限驗證和分流操作,這里我們只關注 身份驗證方法,self.perform_authentication(),該方法內只有一句代碼request.user,看起來像是在調用request對象的user屬性,其實不然,我們可以到DRF框架的Request對象中找到以下方法。

@property
 def user(self):
  """
  Returns the user associated with the current request, as authenticated
  by the authentication classes provided to the request.
  """
  if not hasattr(self, '_user'):
   with wrap_attributeerrors():
    self._authenticate()
  return self._user

user方法經過property裝飾器裝飾后,就可以像一個屬性一樣調用該方法,該方法在Request對象中存在對應的user時會直接返回,若用戶登陸時,Request對象中沒有對應的user,所以代碼會走if判斷里面,我們只需要關注方法self._authenticate()的調用即可。

def _authenticate(self):
  """
  Attempt to authenticate the request using each authentication instance
  in turn.
  """
  for authenticator in self.authenticators:
   try:
    user_auth_tuple = authenticator.authenticate(self)
   except exceptions.APIException:
    self._not_authenticated()
    raise

   if user_auth_tuple is not None:
    self._authenticator = authenticator
    self.user, self.auth = user_auth_tuple
    return

  self._not_authenticated()

可以看到,該方法會遍歷我們在之前處理Request對象時傳入的裝著驗證類對象的列表,并調用驗證類的authenticate()方法,若驗證成功生成對應的self.user和self.auth并直接return,往上則直接將生成的self.user進行返回,若驗證內部出錯,會調用 self._not_ authenticated(), 并拋出錯誤,往上看,在dispatch方法中,若初始化方法出錯,則進行捕獲,并調用self.handle_exception()方法生成一個Response對象進行返回,不會執行視圖類中對應的方法,則調用對于的是self._not_authenticated()。

def _not_authenticated(self):
  """
  Set authenticator, user & authtoken representing an unauthenticated request.

  Defaults are None, AnonymousUser & None.
  """
  self._authenticator = None

  if api_settings.UNAUTHENTICATED_USER:
   self.user = api_settings.UNAUTHENTICATED_USER()
  else:
   self.user = None

  if api_settings.UNAUTHENTICATED_TOKEN:
   self.auth = api_settings.UNAUTHENTICATED_TOKEN()
  else:
   self.auth = None

UNAUTHENTICATED_USER在django默認配置中為一個匿名用戶的類,UNAUTHENTICATED_TOKEN默認為None,若所有驗證都為通過或者某一驗證過程中出錯,則生成一個匿名用戶,并將self.auth設置為None。

綜上所述,在請求執行之前,DRF框架會根據我們在配置文件中配置的驗證類對用戶進行身份驗證,若未通過驗證,則會生成一個匿名用戶,驗證通過,則生成對應的用戶。

“django drf框架怎么實現user驗證以及JWT的構成”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

亚洲午夜精品一区二区_中文无码日韩欧免_久久香蕉精品视频_欧美主播一区二区三区美女