用戶許可權驗證源碼剖析,和用戶登錄驗證有點相似,但是為了增加記憶,有必要再一次添加, 註意:一定要跟著博主的解說再看代碼的中文註釋及其下麵的一行代碼!!! 1、準備一個路由和視圖類,全局路由配置暫時忽略,當流程執行到下麵的url:groupsSelectAll——> GroupsView的視圖類下的a ...
用戶許可權驗證源碼剖析,和用戶登錄驗證有點相似,但是為了增加記憶,有必要再一次添加,
註意:一定要跟著博主的解說再看代碼的中文註釋及其下麵的一行代碼!!!
1、準備一個路由和視圖類,全局路由配置暫時忽略,當流程執行到下麵的url:groupsSelectAll——> GroupsView的視圖類下的as_view()方法
from django.conf.urls import url
from . import views
app_name = '[words]'
urlpatterns = [
url(r'groupsSelectAll/', views.GroupsView.as_view(), name="groupsSelectAll"), # 片語信息查詢所有
]
class GroupsView(APIView):
def get(self, request):
conditions = {
"id": request.query_params.get("wid"),
"name": request.query_params.get("name"),
"start_time": request.query_params.get("start_time"),
"end_time": request.query_params.get("end_time"),
}
res = DataManager.select_by_conditions("words_groups", None, **conditions)
return Response(data={"code": 200, "result": res})
2、但是GroupsView類下沒有as_view方法,這時就要去它的父類APIView查看(點進去看as_view方法),這裡博主只複製方法源代碼,大家只需要看中文註釋及其下的代碼語句。在這個方法中值得一提的是super關鍵字,如果請求視圖類(就是GroupsView類,如果繼承了多個父類)還有另一個父類,它先會查看這個父類是否有as_view方法。在這裡它是會執行APIView的父類View中的as_view方法,然後我們再次查看父類View的as_view方法。第一個as_view方法是APIView類的,第二個as_view方法是View類的。
@classmethod def as_view(cls, **initkwargs): """ Store the original class on the view function. This allows us to discover information about the view when we do URL reverse lookups. Used for breadcrumb generation. """ if isinstance(getattr(cls, 'queryset', None), models.query.QuerySet): def force_evaluation(): raise RuntimeError( 'Do not evaluate the `.queryset` attribute directly, ' 'as the result will be cached and reused between requests. ' 'Use `.all()` or call `.get_queryset()` instead.' ) cls.queryset._fetch_all = force_evaluation # 執行父類的as_view方法 view = super(APIView, cls).as_view(**initkwargs) view.cls = cls view.initkwargs = initkwargs # Note: session based authentication is explicitly CSRF validated, # all other authentication is CSRF exempt. return csrf_exempt(view)APIView.as_view()
@classonlymethod def as_view(cls, **initkwargs): """Main entry point for a request-response process.""" for key in initkwargs: if key in cls.http_method_names: raise TypeError("You tried to pass in the %s method name as a " "keyword argument to %s(). Don't do that." % (key, cls.__name__)) if not hasattr(cls, key): raise TypeError("%s() received an invalid keyword %r. as_view " "only accepts arguments that are already " "attributes of the class." % (cls.__name__, key)) # 執行view方法 def view(request, *args, **kwargs): # 這裡的cls就是我們的請求視圖類,顯而易見,這個self是請求試圖類的對象 self = cls(**initkwargs) if hasattr(self, 'get') and not hasattr(self, 'head'): self.head = self.get self.request = request self.args = args self.kwargs = kwargs # 然後這裡就是執行dispatch方法 return self.dispatch(request, *args, **kwargs) view.view_class = cls view.view_initkwargs = initkwargs # take name and docstring from class update_wrapper(view, cls, updated=()) # and possible attributes set by decorators # like csrf_exempt from dispatch update_wrapper(view, cls.dispatch, assigned=()) return viewView.as_view()
3、我們在第二個as_view方法中可以知道self是我們的請求視圖類的對象,通過這個self調用dispatch方法,請求視圖類中沒有dispatch方法,是不是又去APIView類中執行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對象 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: # 通過python的反射機制反射到請求視圖類的方法名稱 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.responseAPIView.dispatch()
4、其他代碼不用看,我們直接看initial方法,因為這個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 # 用戶驗證的方法,這個request 是加工之後的request self.perform_authentication(request) # 用戶許可權驗證 self.check_permissions(request) # 用戶訪問頻率限制 self.check_throttles(request)APIView.initial()
5、這就到了我們的用戶許可權驗證的戲碼了。博主添加APIView部分代碼,即check_permission方法用到的代碼。我們可以查看代碼中的self.check_permissions(request),點進去查看check_permissions()方法,可以看到有get_permissions方法,這個方法有self.permission_classes變數,即self.permission_classes = api_settings.DEFAULT_PERMISSION_CLASSES,然後這裡也和【上一篇的用戶登錄驗證】很相似,就是請求視圖類中如果沒有這個變數名及值(值是一個列表),就會使用全局配置文件中的REST_FRAMEWORK={"DEFAULT_PERMISSION_CLASSES":["許可權驗證類的全路徑"]},或者我們在請求視圖類中添加這個變數及值
class APIView(View): # 如果請求視圖類中沒有這個變數和值,就會使用全局配置文件的值 permission_classes = api_settings.DEFAULT_PERMISSION_CLASSES def check_permissions(self, request): """ Check if the request should be permitted. Raises an appropriate exception if the request is not permitted. """ # 迴圈許可權類的對象 for permission in self.get_permissions(): # 執行對象下的has_permission()方法,驗證是否有許可權(預設是有許可權) if not permission.has_permission(request, self): self.permission_denied( request, message=getattr(permission, 'message', None) ) def get_permissions(self): """ Instantiates and returns the list of permissions that this view requires. """ # 返回的是一個許可權類的對象 return [permission() for permission in self.permission_classes] def permission_denied(self, request, message=None): """ If request is not permitted, determine what kind of exception to raise. """ # 有許可權類,但是沒有登錄,就拋出用戶沒有登錄的異常 if request.authenticators and not request.successful_authenticator: raise exceptions.NotAuthenticated() # 拋出沒有許可權的異常 raise exceptions.PermissionDenied(detail=message)APIView
6、在上面的APIView類中會執行到if not permission.has_permission(request, self),我們可以直接點進去查看has_permission方法。一般我們自定義這個用戶許可權驗證類的話我們一般需要繼承BasePermission類,這樣我們直接重寫has_permission方法,裡面的需求就是驗證時候當前登錄的用戶是否具有某個模塊的許可權。像這樣我們可以自定義一個只有充值稱為VIP或者SVIP用戶訪問許可權的類(這隻是我的設想)