首先得有一点常识,比如用户认证,就是authenticate

比如一个函数,应该有返回值,

比如一个类里面的self,真的是代表本身这个类吗

再比如看到一个东西加括号,就两种情况,一种是函数,一种是类,区分函数和类,就看加括号之后的东西,是否还调用属性或者方法

还有如果你用pycharm,看源码就很方便了,看到一个属性或方法,你需要做的就是按住ctrl点进去就行了

假设你已经知道,dispatch函数是源码的入口,然后我们就可以愉快的交流了

下面演示如何使用rest_framework的用户认证功能,以及为什么要这样做

1.写路由匹配

  因为是cbv视图,所以用as_view()  

from django.conf.urls import url
from django.contrib import admin
from app01 import views urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^test/', views.TestView.as_view()),
]

2.CBV风格视图

from django.shortcuts import HttpResponse

from rest_framework.views import APIView#导入rest_framework的试图类

class TestView(APIView):
  self.dispatch      #只是为了进源码
def get(self,request,*args,**kwargs):
return HttpResponse('ok')

好了,最基本的框架就这样搭好了,我们开始看源码,源码的入口是在   APIView 的dispatch方法,所以我们点进去

dispatch函数:

    def dispatch(self, request, *args, **kwargs):#这是APIView类的入口
"""
`.dispatch()` is pretty much the same as Django's regular dispatch,
but with extra hooks for startup, finalize, and exception handling.
和Django的dispatch类似,但是增加了额外的钩子,比如开始,结束,以及异常的处理
"""
self.args = args #封装参数
self.kwargs = kwargs
request = self.initialize_request(request, *args, **kwargs) #重新封装了request
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和self.initial初识化这两个地方

1.1先看重新封装request这个

    def initialize_request(self, request, *args, **kwargs):
"""
Returns the initial request object.
返回初始化后的request
"""
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
)

1.2

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

这个函数返回了一个列表,通过列表生成式,循环    self.authentication_classes   ,然后拿到的东西实例化

tips:看到一个东西加括号,就两种情况,一种是函数,一种是类,区分函数和类,就看加括号之后的东西,是否还调用属性或者方法

1.3

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      #
throttle_classes = api_settings.DEFAULT_THROTTLE_CLASSES

坏了,这下不能点了,看settings,说明这是配置信息,好了,看request看到这就走不下去了,因为不能点了,现在我们拿到的信息是:

  在封装request的时候, 把一个auth()列表传到了request的参数里面,然后这个auth()是从这个self.authentication_classes 里面拿到的

现在我们可以打印一下这个  self.authentication_classes,self就是APIview 嘛,我门的视图函数就是继承了这个类啊,所以,打印一下:

拿到了这个:

[<class 'rest_framework.authentication.SessionAuthentication'>, <class 'rest_framework.authentication.BasicAuthentication'>]

这种情况应该兴奋,因为真的有东西,拿到的是个路径,然后我们就可以用这个路径拿到这两个类

并且,验证了self.authentication_classes  就是个列表,然后里面是一个一个类

如果我们自己写这个authentication_classes的话,也要写成一个列表,里面是一个个类,下面看一下这个类要怎么写

1.4

然后我们先导入一下这个路径,看一下里面写的啥,

from rest_framework.authentication import SessionAuthentication

点进去

是一个新的文件

总体看一下,这个文件有这样几个类

然后都继承了BaseAuthentication类,先看一下这个类

1.5

class BaseAuthentication(object):
"""
All authentication classes should extend BaseAuthentication.
""" def authenticate(self, request):
"""
Authenticate the request and return a two-tuple of (user, token).
"""
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.
"""
pass

没有什么实质性的东西,但是规定了继承这个类的子类,必须要有这个   authenticate   方法,否则就会报错,所以我们就知道了应该怎么自己写这个类了

那就可以猜到,下面继承了这个基类的几个子类就是不同的认证方法了

看了这个基类,大概知道自己定义的类里面必须要写这样一个authenticate   方法,不写会报错,但是应该怎么写呢?需要返回什么呢,其实重点是要返回什么

我们现在才走了一半,只看了封装request这一部分,还有初始化呢,重新点进dispatch函数,找到

2.1

    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)      -------执行认证

注意这里传了参数,是封装后的request

2.2

    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方法,

2.3

    @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'):
self._authenticate()
return self._user

果不其然,是一个加了装饰器的方法,我们的request里面没有这个_user属性,所以执行的是   self._authenticate()  这个方法

2.4

    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()

循环   self.authenticators,拿到   authenticator 并执行 authenticator 里面的  authenticate 方法,参数为request

注意, self.authenticators  这个东西在1.1中就已经拿到了,这个就是我们要写的验证的类,然后跟我们得到的结论也一样,有一个authentic 方法

2.5 看看这个方法,authentic

    def authenticate(self, request):
return (self.force_user, self.force_token)

哦,返回一个元组,大概就是  用户和token,现在这两行代码是如果有指定值的时候的情况,但是可以说明问题

再看看1.4里面那几个验证的子类,基本上就确认是返回一个已通过认证的用户和对应的token

这是通过认证的情况

2.6再看2.4里面,最后那个     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

没有默认值的话,是返回None的,但是是有默认值的

点进去api_settings ,是个类

api_settings = APISettings(None, DEFAULTS, IMPORT_STRINGS)

再点进去,就是rest_framework的配置文件

搜一下,就可以看到这条配置

'UNAUTHENTICATED_USER': 'django.contrib.auth.models.AnonymousUser',
'UNAUTHENTICATED_TOKEN': None,

默认是调用Django的匿名用户类,所以默认如果不登录,是匿名用户

还能说明一个问题,不登录也能访问,即匿名用户访问是否被允许,就可以在认证类中配置了,返回None,就是允许,抛出异常就是不允许

贴一个完整的代码

from django.shortcuts import HttpResponse

from rest_framework.views import APIView#导入rest_framework的试图类

from rest_framework.authentication import BaseAuthentication
from rest_framework import exceptions
from . import models
class CustomAuthenticate(BaseAuthentication):
"""
All authentication classes should extend BaseAuthentication.
""" def authenticate(self, request):
"""
Authenticate the request and return a two-tuple of (user, token).
"""
tk = request.query_param.get('tk') #获取用户输入的token 简化写法,就直接在url中传参了
token_obj = models.Token.objects.filter(token=tk).first()
if token_obj:
return (token_obj.user,token_obj) raise exceptions.AuthenticationFailed('请登录') #抛出异常表示不允许匿名用户访问 # else:
# return (None,None)
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.
"""
pass
class TestView(APIView):
authentication_classes = [CustomAuthenticate,] #应用认证配置类
def get(self,request,*args,**kwargs):
return HttpResponse('ok')

源码怎么找之rest_framework的用户认证的更多相关文章

  1. javac编译不同目录的源码提示找不到符号

    对于单个文件的且不引用其他类文件的java源码用javac编译大家都很熟悉即 javac mycode.java 但是如果这个文件引用到了其他的类文件,在进行编译的时候就会提示找不到符号,这时我们需要 ...

  2. (转)OpenFire源码学习之七:组(用户群)与花名册(用户好友)

    转:http://blog.csdn.net/huwenfeng_2011/article/details/43413651 Group 在openfire中的gorop——组,也可以理解为共享组.什 ...

  3. Android 4.1源码编译找不到资源文件解决办法

    我们在Android framework中修改资源文件时,在Android 4.0之前,都是直接在sourcecode/frameworks/base/core/res/res下面添加对应的资源文件, ...

  4. [Go] gocron源码阅读-判断是否使用root用户执行

    判断是linux系统,并且uid为0,allowRoot是通过命令行传参传进来的,通过flag包解析出来的,可以使用go run node.go -h看到这些参数 && !allowR ...

  5. Rest_framework Router 路由器(含SimplyRouter源码浅解)

    目录 Rest_framework Router 路由器 ViewSet结合Router,自动生成url. 将ViewSet注册到Router中,需要三个要素: 关于路由规则,细分有四类: rest_ ...

  6. v79.01 鸿蒙内核源码分析(用户态锁篇) | 如何使用快锁Futex(上) | 百篇博客分析OpenHarmony源码

    百篇博客分析|本篇为:(用户态锁篇) | 如何使用快锁Futex(上) 进程通讯相关篇为: v26.08 鸿蒙内核源码分析(自旋锁) | 当立贞节牌坊的好同志 v27.05 鸿蒙内核源码分析(互斥锁) ...

  7. Django rest framework 源码分析 (1)----认证

    一.基础 django 2.0官方文档 https://docs.djangoproject.com/en/2.0/ 安装 pip3 install djangorestframework 假如我们想 ...

  8. 2014年6月份第1周51Aspx源码发布详情

    企业汽车服务终端管理系统源码  2014-6-3 [VS2010]源码描述:本系统专门服务于(汽车美容4s店) 完整的一套汽车美容管理服务系统. 功能介绍:汽车美容服务终端功能强大而又简便实用,界面友 ...

  9. 【腾讯Bugly干货分享】微信Tinker的一切都在这里,包括源码(一)

    本文来自于腾讯bugly开发者社区,非经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/57ecdf2d98250b4631ae034b 最近半年以来,Android热补 ...

随机推荐

  1. dmesg和addr2line 定位 segfault

    程序长时间运行崩溃,但是没有保存core dump消息.可以用下面的方法定位出程序出错位置: 1. 用dmesg查找出错的代码段地址 ip 000000000041ccec 发生错误时指令的地址, s ...

  2. ReactiveCocoa_v2.5 源码解析之架构总览

    ReactiveCocoa 是一个 iOS 中的函数式响应式编程框架,它受 Functional Reactive Programming 的启发,是 Justin Spahr-Summers 和 J ...

  3. migo的增强

    migo的增强 所用BADI:MB_MIGO_BADI   具体见例子:(SE19) CIN_PLUG_IN_TO_MIGO JVA_CRP_MIGO_BADI JV_CRP_MIGO_BADI WB ...

  4. Ubuntu16.04下Office替代品Office Online

    Ubuntu16.04下Office替代品 Ubuntu16.04下的office Libreoffice 这个是Ubuntu自带的Office,总是存在各种问题,如果用来阅读还是不错的,但是编辑就不 ...

  5. Appium python自动化测试系列之Android UIAutomator终极定位(七)

    android uiautomator text定位 可能有人不知道为什么说android uiautomator是终极定位,而且android uiautomator和appium有什么关系呢?如果 ...

  6. Python学习笔记(八)

    Python学习笔记(八): 复习回顾 递归函数 内置函数 1. 复习回顾 1. 深浅拷贝 2. 集合 应用: 去重 关系操作:交集,并集,差集,对称差集 操作: 定义 s1 = set('alvin ...

  7. mysql多实例-主从复制安装

    安装环境:Centos6.5 mysql版本:mysql-5.5.32.tar.gz 一:安装前准备: 1.安装一些依赖库 yum install cmake gcc gcc-c++ ncurses- ...

  8. day38(增强类的实现)

    定义一个接口 package com.baidu.test; public interface Person { public abstract void eat(); public abstract ...

  9. LINUX 笔记-grep命令

    grep [-acinv] [--color=auto] '查找字符串' filename 它的常用参数如下: -a :将binary文件以text文件的方式查找数据 -c :计算找到'查找字符串'的 ...

  10. 网页单位和rem小分享

    有哪些网页尺寸单位? CSS 中的单位有很多种: 百分比(%) 英寸(in) 厘米(cm) 毫米(mm) 磅数(pt) 12 点活字(pc) 字母高度一半(ex) 父级字体(em) 像素(px) 根元 ...