django 2.0官方文檔python
https://docs.djangoproject.com/en/2.0/
pip3 install djangorestframework
首先先建立一個應用 django
python manage.py startapp app01
在應用 app01.views.py 下 的視圖函數的代碼以下json
from rest_framework.views import APIViewfrom rest_framework import exceptions from rest_framework.request import Request class MyAuthentication(object): def authenticate(self,request): token = request._request.GET.get('token') # 獲取用戶名和密碼,去數據校驗 if not token: raise exceptions.AuthenticationFailed('用戶認證失敗') return ("dog",None) def authenticate_header(self,val): pass class DogView(APIView): authentication_classes = [MyAuthentication,] def get(self,request,*args,**kwargs): print(request) print(request.user) self.dispatch ret = { 'code':1000, 'msg':'xxx' } return HttpResponse(json.dumps(ret),status=201)
在項目的根目錄下的urls.py 中添加路由的代碼以下:api
from app01 import views urlpatterns = [ url(r'^dog/', views.DogView.as_view()), ]
若是想實現必須是登錄後才能進行請求的話,只需重寫父類中的 authenticate (進行一些邏輯認證)和 authenticate_header 就能達到認證的效果瀏覽器
啓動項目,在瀏覽器中輸入app
http://127.0.0.1:8000/dog/?token=123
‘攜帶token返回的結果以下iview
{"code": 1000, "msg": "xxx"}
沒有攜帶token返回的結果以下ide
http://127.0.0.1:8000/dog
那麼上面的源碼內部又是如何是現代的呢函數
源碼流程圖源碼分析
源碼入口先從 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進行封裝 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
能夠看出上面的代碼中對原生的request進行了封裝
request = self.initialize_request(request, *args, **kwargs) self.request = request
那麼它內部作了些什麼呢 ,追蹤 self.initialize_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() 這個貌似和認證有關係,追蹤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]
經過上面的代碼咱們能夠看到 其返回的是經過列表生成式返回的一個實例化對象 那麼它是經過什麼生成這個對象的呢,下面咱們來繼續追蹤 self.authentication_classes 其代碼以下所示:
class APIView(View): # The following policies may be set at either globally, or per-view. renderer_classes = api_settings.DEFAULT_RENDERER_CLASSES parser_classes = api_settings.DEFAULT_PARSER_CLASSES authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES
到這裏咱們發現它是經過讀取應用的配置文件,到這裏神祕的面紗已經開始解開了,加入咱們本身寫的類若是本身的內部有 authentication_classes 這個屬性,而且其也是一個可迭代的對象,那麼根據面向對象的屬性,就會執行咱們
本身的self.authentication_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 的意思是執行認證的意思,讓咱們追蹤其內部的代碼以下:
def perform_authentication(self, request): """ Perform authentication on the incoming request. Note that if you override this and simply 'pass', then authentication will instead be performed lazily, the first time either `request.user` or `request.auth` is accessed. """ request.user
看到其調用的是對原始request封裝後的user ,追蹤其 內部的user發現其是一個實例屬性,代碼以下所示
點進去能夠看到Request有個user方法
@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
讓咱們進行追蹤 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()
這裏分三種狀況
1.若是authenticate方法拋出異常,self._not_authenticated()執行後在拋出異常-----就是認證沒經過
2.有返回值,必須是元組:(request.user,request.auth)
3.返回None,表示當前認證不處理,等下一個認證來處理
返回值就是例子中的:
token_obj.user-->>request.user token_obj-->>request.auth
經過認證自定義的authenticated沒有返回值,就執行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() #AnonymousUser匿名用戶 else: self.user = None if api_settings.UNAUTHENTICATED_TOKEN: self.auth = api_settings.UNAUTHENTICATED_TOKEN() #None else: self.auth = None
由於authenticate方法咱們本身重寫了,因此當執行authenticate()的時候就是執行咱們本身寫的認證
父類中的authenticate方法
def authenticate(self, request): return (self.force_user, self.force_token)
咱們本身寫的
def authenticate(self,request): token = request._request.GET.get('token') # 獲取用戶名和密碼,去數據校驗 if not token: raise exceptions.AuthenticationFailed('用戶認證失敗') return ("dog",None)
以上的是咱們本身定義的 authentication_classes 它會去咱們的類屬性中使用咱們本身的,只需在須要認證接口的中設置類屬性 authentication_classes = [ 認證的邏輯函數 ]
假如咱們進行全局認證的話,能夠在配置文件中設置,經過繼承父類的方式來實現,簡單的說就是 類屬性 authentication_classes 本身不定義使用父類的,看如下的代碼:
class APIView(View): # The following policies may be set at either globally, or per-view. renderer_classes = api_settings.DEFAULT_RENDERER_CLASSES parser_classes = api_settings.DEFAULT_PARSER_CLASSES authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES
在以上的代碼中咱們能夠看到authentication_classes = = api_settings.DEFAULT_AUTHENTICATION_CLASSES 咱們能夠追蹤看到代碼以下:
def reload_api_settings(*args, **kwargs): setting = kwargs['setting'] if setting == 'REST_FRAMEWORK': api_settings.reload() setting_changed.connect(reload_api_settings)
這是它會去配置文件中讀取 REST_FRAMEWORK ,這時後咱們能夠在配置文件中添加這個配置,裏面的鍵是 DEFAULT_PARSER_CLASSES 值是認證函數的路徑
REST_FRAMEWORK = { # 全局使用的認證類 "DEFAULT_AUTHENTICATION_CLASSES":['api.utils.auth.Authtication', ], }
咱們根據上述配置的路徑,在應用api創建如下文件 utils/auth.py 代碼以下:
class Authtication(BaseAuthentication): def authenticate(self,request): token = request._request.GET.get('token') token_obj = models.UserToken.objects.filter(token=token).first() if not token_obj: raise exceptions.AuthenticationFailed('用戶認證失敗') # 在rest framework內部會將整個兩個字段賦值給request,以供後續操做使用 return (token_obj.user, token_obj) def authenticate_header(self, request): pass
這時後全部的接口,必須通過認證後才能訪問,可是有的接口是不須要認證的,好比登錄的時候這時,咱們能夠在登錄的接口中不走父類(apiview) 的認證使用咱們本身 的,只需定義定義類屬性authentication_classes = [ ] 便可
class AuthView(APIView): """ 用於用戶登陸認證 """ authentication_classes = [] def post(self,request,*args,**kwargs): pass
有些時候用戶是在沒有登錄的時候,訪問咱們的接口,這時候咱們稱之爲匿名的用戶,咱們能夠對其進行簡單的設置
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() 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
在上面的源碼中咱們能夠看到 匿名用戶默認的request.user=AnonymousUser, request.auth = None ,咱們來寫一個接口來簡單的認證如下
class UserInfoView(APIView): authentication_classes = [] def get(self,request,*args,**kwargs): print(request.user) print(request.auth ) return HttpResponse('用戶信息')
爲其 配置url
url(r'^api/v1/info/$', views.UserInfoView.as_view()),
在瀏覽器中輸入
http://127.0.0.1:8000/api/v1/info/
測試的結果以下:
源碼中的user 和token 的設置以下:
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()
咱們能夠看到 對user 和 token 的設置,咱們能夠定義一個函數,因此咱們能夠這樣設置,
REST_FRAMEWORK = { # 全局使用的認證類 "DEFAULT_AUTHENTICATION_CLASSES":['api.utils.auth.FirstAuthtication','api.utils.auth.Authtication', ], # "UNAUTHENTICATED_USER":lambda :"匿名用戶", "UNAUTHENTICATED_USER": None "UNAUTHENTICATED_TOKEN":None, }
測試的結果以下:
rest_framework裏面內置了一些認證,咱們本身寫的認證類最好都要繼承內置認證類 "BaseAuthentication"
from rest_framework.authentication import BasicAuthentication
class BaseAuthentication(object): """ All authentication classes should extend BaseAuthentication. """ def authenticate(self, request): """ Authenticate the request and return a two-tuple of (user, token). """ #內置的認證類,authenticate方法,若是不本身寫,默認則拋出異常 raise NotImplementedError(".authenticate() must be overridden.") def authenticate_header(self, request): """ Return a string to be used as the value of the `WWW-Authenticate` header in a `401 Unauthenticated` response, or `None` if the authentication scheme should return `403 Permission Denied` responses. """ #authenticate_header方法,做用是當認證失敗的時候,返回的響應頭 pass
rest_framework裏面還內置了其它認證類,咱們主要用到的就是BaseAuthentication,剩下的不多用到
本身寫認證類方法梳理
(1)建立認證類
(2)authenticate()返回值(三種)
(3)局部使用
(4)全局使用
#設置全局認證 REST_FRAMEWORK = { "DEFAULT_AUTHENTICATION_CLASSES":['API.utils.auth.Authentication',] }