前言:
在我的上一篇博客我介绍了一下单独为某条 url 设置认证, 但是如果我们想对所有的 url 设置认证, 该怎么做呢? 我们这篇博客就是给大家介绍一下在 Rest_framework 中如何实现全局的设置认证组件的功能. 下面就请大家跟着我的思路看博客
如果有对局部设置不清楚的, 可以看我的上一篇博客, 源码级的分析单独设置 Rest_framework 的认证组件: https://www.cnblogs.com/bainianminguo/p/10480887.html
正文:
我们在走一步流程
1, 进入 urls 路由文件
url(r'^login/', views.LoginCBV.as_view(),name="login"),
2, 进入 as_view 这个方法, 这个方法被类直接调用, 那么这个方法一定会被 classmethod 修饰符修饰, 是一个类方法
- @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
- 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)
3,as_view 这个方法, 我们看到返回值为 view 的方法的返回值, 而 view 这个方法又是什么? 我们在 as_view 方法中看到这样一段代码, 就是执行父类的 as_view 的方法
view = super(APIView, cls).as_view(**initkwargs)
4, 进入 APIView 的父类的 as_view 方法, 也就是 View 类的 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))
- def view(request, *args, **kwargs):
- 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
- 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 view
5, 下面重点来分析 View 类的 as_view 方法, 这个 as_view 方法返回是一个 view 方法的执行的结果, 而 view 方法又干了什么, 我们看下 view 方法, 这个方法返回的是 dispatch 方法
- def view(request, *args, **kwargs):
- 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
- return self.dispatch(request, *args, **kwargs)
6, 下面我们看下 dispatch 方法, 这个 dispatch 方法是 APIView 这个类的方法
- 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
7, 先看这里, 将源生的 request 进行初始化
8, 看下 initialize_request 方法, 这个方法返回了一个新的 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
- )
9, 因为我们这里在做认证的源码分析, 我们重点看下 authenticators 这个属性的, 也就是 get_authenticators 方法, 这里要非常的注意, 这里非常的关键, 就是有 self.authentication_classes 这个属性
- def get_authenticators(self):
- """
- Instantiates and returns the list of authenticators that this view can use.
- """
- return [auth() for auth in self.authentication_classes]
重点是这个属性, 大家一定要记住
10, 下面我们接着步骤 7 在往后执行, 看下 initial 这个方法
11, 进入这个 initial 这个方法, 这里有 3 个组件, 认证, 权限, 频率, 我们重点看认证这个组件
- 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)
12, 看下认证组件的方法 perform_authentication 这个方法, 返回一个 request.user 这个, request 是什么, 我们看到在执行 initial 方法的时候, 传了一个 request 进去, 这个 request 就是 request.user 的这个 request
- 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
13, 我们在汇过去看下 inital 方法传递的参数 request, 我们看到 initial 方法的 request 是 initalize_request 方法执行的结果
14, 下面看下 initalize_request 这个方法返回的是什么? 返回了一个 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
- )
15, 进入 Request 类, 我们看下的 user 属性或者方法, 看代码, 发现这是一个被 propery 修饰过的方法, 调用这个方法的方法和调用属性的方法一样
- @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
16,Request 这个类, 我们看了下没有_user 这个属性, 所以会进入 if 的条件语句, 下面我们看下_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()
17, 这个方法有些参数大家可能也不清楚, 我们在回答一下, 先看下 authenticators, 由于这个 self 是 Request 类的一个实例对象
for authenticator in self.authenticators:
我们看下实例化 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 这个值就是 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_classe
authentication_classes 就是我们自己的配置的认证类
18, 在看下 authenticator.authenticate 这个方法
19, 然后这个时候就可以看下面我们的认证配置
首先定义了一个认证类, 这个认证类有 authenticate 方法, 这个方法的返回值为一个元组, 我已经圈起来来
- class Book_auther(BaseAuthentication):
- def authenticate(self,request):
- token = request.GET.get("token")
- token_obj = models.Token.objects.filter(token=token).first()
- if token_obj:
- return token_obj.user.name,token_obj.token
- else:
- raise exceptions.AuthenticationFailed("验证失败")
- def authenticate_header(self,request):
- pass
然后在我们自己的视图类中定义了认证的类的列表, 实例化我们的认证类
- class Book_cbv(APIView):
- authentication_classes = [Book_auther,]
- def get(self,request):
- query_list = models.Book.objects.all()
- # bs = book_serializers(query_list,many=True)
- bs = bookmodelserializer(query_list,many=True,context={'request': request})
- return Response(bs.data)
- def post(self,request):
- bs = bookmodelserializer(data=request.data)
- print(request.data)
- if bs.is_valid():
- print(bs.validated_data)
- bs.save()
- return Response(bs.data)
- else:
- return Response(bs.errors)
20, 这个时候, 我们才能正式进入认证类的全局配置的地方, 做全局配置, 我们当然不能在每个视图类中配置, 那么如果我们不配置这个 authentication_classes 这个属性呢?
其实 APIView 默认是有这个参数, 如果我们没有配置, 则用 APIView 这个类的属性
21, 看到这句代码大家可能不懂, 如果看不懂, 大家可以下我的这篇博客, 介绍面向对象的__getattr__方法的作用
博客地址: https://www.cnblogs.com/bainianminguo/p/10475204.html
authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES
22, 下面我们进入 api_settings 这个实例, 我们看到 api_settings 这个是 APISettings 类的实例对象
api_settings = APISettings(None, DEFAULTS, IMPORT_STRINGS)
23, 然后我们在看下 APISettings 这个类
- class APISettings(object):
- """
- A settings object, that allows API settings to be accessed as properties.
- For example:
- from rest_framework.settings import api_settings
- print(api_settings.DEFAULT_RENDERER_CLASSES)
- Any setting with string import paths will be automatically resolved
- and return the class, rather than the string literal.
- """
- def __init__(self, user_settings=None, defaults=None, import_strings=None):
- if user_settings:
- self._user_settings = self.__check_user_settings(user_settings)
- self.defaults = defaults or DEFAULTS
- self.import_strings = import_strings or IMPORT_STRINGS
- self._cached_attrs = set()
24, 看了我的博客, 就会知道__getattr__这个方法的使用场景, 也就知道下面这段代码实际就会执行 APISettings 类的__getattr__方法
authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES
25, 下面我们看下 APISettings 类的__getattr__方法
- def __getattr__(self, attr):
- if attr not in self.defaults:
- raise AttributeError("Invalid API setting:'%s'" % attr)
- try:
- # Check if present in user settings
- val = self.user_settings[attr]
- except KeyError:
- # Fall back to defaults
- val = self.defaults[attr]
- # Coerce import strings into classes
- if attr in self.import_strings:
- val = perform_import(val, attr)
- # Cache the result
- self._cached_attrs.add(attr)
- setattr(self, attr, val)
- return val
在看下 user_settings 是是否有 DEFAULT_AUTHENTICATION_CLASSES 这个 k 值
然后看 user_settings 这个方法
下面就是在实例化 APISettings 类的时候代码, 第一个参数 user_settings, 这个参数的值 None
所以 APISettings 这个类的实例对象没有_user_settings 这个属性, 所以会进入 if 的流程中
这个 settings 是什么呢? 其实就是 Djaong 的 project 的 settings 文件
所以我们就需要在 settings 中配置 "REST_FRAMEWORK" 这个属性的值为一个字典, 我们看到后面如果拿不到 "REST_FRAMEWORK" 就会给赋值给空的字典
26, 但是到了这里, 我们字典该怎么写呢? 大家一脸的懵逼了, 我们可以看下这段代码, 如果我们在 settings 没有配置 "REST_FRAMEWORK" 就会走下面的流程, 我们看下面的流程的是什么东西
27,self.defaults 是什么, 就是我们在实例时候 APISetings 这个类的时候传递的参数
在来回忆一下, 实例化 APISettings 类
在看下初始化 APISettings 类的时候__init__方法
我们看下 DEFAULTS 是什么, 就是下面的配置
我们模仿上面写我们的 REST_FRAMEWORK
- REST_FRAMEWORK = {
- "DEFAULT_AUTHENTICATION_CLASSES":(
- "app1.utils.Book_auther",
- )
- }
28, 我们使用 postman 进行测试
先使用 get 的方式访问 book_cbv
然后使用 post 方式访问 book_cbv
最后使用 post 访问 book_detail_cbv
最后我们加上 token 在访问一次, 访问了 2 个 url, 均可以访问成功
至此 Rest_framework 全局设置认证组件的源码剖析和实现我们多讲完了, 大家请查看, 并给出意见
来源: https://www.cnblogs.com/bainianminguo/p/10487059.html