Django rest framework 的认证流程(源码分析)
一、基本流程举例:
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^users/', views.HostView.as_view()), ]
urls
from rest_framework.views import APIView
from rest_framework.response import Response
class HostView(APIView):
def dispatch(self, request, *args, **kwargs):
"""
请求到来之后,都要执行dispatch方法,dispatch方法根据请求方式不同触发 get/post/put等方法 注意:APIView中的dispatch方法有好多好多的功能
"""
return super().dispatch(request, *args, **kwargs) def get(self, request, *args, **kwargs):
return Response('GET请求,响应内容') def post(self, request, *args, **kwargs):
return Response('POST请求,响应内容') def put(self, request, *args, **kwargs):
return Response('PUT请求,响应内容')
Views
二、源码分析:
当我们发送请求后,执行views里面对应的方法时,最开始执行的是dispatch方法
---------------------------------------------------------------------------------- 插叙-------------------------------------------------------------------------------------------------------------------
为什么是diapach方法?
我们使用rest framework 框架是基于CBV做的,在url中
url(r'^hosts/', views.HostView.as_view()),
url(r'^auth/', views.AuthView.as_view()),
url(r'^users/', views.Userview.as_view()),
url(r'^sals/', views.Salview.as_view()),
进入as_view()
APIView类中的as_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 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)
进入原生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
可以看到最后返回的是
return self.dispatch(request, *args, **kwargs)
所以访问views中的类是先执行dispatch方法,然后再用调用 其他方法
---------------------------------------------------------------------------------- 插叙结束-------------------------------------------------------------------------------------------------------------------
自己定义的dispatch方法(当然自己可以不定义直接应用APIView里面的就可以了,默认就是这种):
def dispatch(self, request, *args, **kwargs):
"""
请求到来之后,都要执行dispatch方法,dispatch方法根据请求方式不同触发 get/post/put等方法 注意:APIView中的dispatch方法有好多好多的功能
"""
return super().dispatch(request, *args, **kwargs)
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
#1.将原来的request进行加工,增加了一些功能,将其放入restframework的Request
'''
request,
parsers=self.get_parsers(),
authenticators=self.get_authenticators(),
negotiator=self.get_content_negotiator(),
parser_context=parser_context
'''
# 原来request对象,django.core.handlers.wsgi.WSGIRequest
# 现在的request对象,rest_framework.request.Request
request = self.initialize_request(request, *args, **kwargs)
self.request = request
self.headers = self.default_response_headers # deprecate? try:
#2.处理版本信息 处理认证信息 处理权限信息 对用户的访问频率进行限制
self.initial(request, *args, **kwargs) # Get the appropriate handler method
#3.据用户提交的请求方法利用反射获取请求方法
#http_method_names=['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']
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) #4.将处理后的response包装
self.response = self.finalize_response(request, response, *args, **kwargs)
return self.response
1.将原来的request进行加工,增加了一些功能,将其放入restframework的Request
request = self.initialize_request(request, *args, **kwargs)
进入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
)
将这些东西前部都封装在rest_framework的Resquest中,并返回她的对象request,从这以后我们调用的request对象不再是Django提供的request对象了,而是APIView的Resquest的对象,2.处理版本信息 处理认证信息 处理权限信息 对用户的访问频率进行限制
self.initial(request, *args, **kwargs)
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.
#2.1处理版本信息
version, scheme = self.determine_version(request, *args, **kwargs)
request.version, request.versioning_scheme = version, scheme # Ensure that the incoming request is permitted
#2.2处理认证信息
self.perform_authentication(request)
#2.3处理权限信息
self.check_permissions(request)
#2.4对用户的访问频率进行限制
self.check_throttles(request)
3.据用户提交的请求方法利用反射获取请求方法,并改用请求方法实现其具体功能
4.将处理后的response进行包装
三、处理认证的具体分析
当diapatch方法进行到第一步时,我们调用了initialize_request方法将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
)
authenticators=self.get_authenticators() #用于用户认证 是一个由authentication对象组成的列表
def get_authenticators(self):
"""
Instantiates and returns the list of authenticators that this view can use.
"""
return [auth() for auth in self.authentication_classes]
authentication_classes为一个authentication类组成的列表,他默认是调用
authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES
当然我们一般是自己定义或者配置到settings中,至此我们的得到authentication对象的列表,其封装在Request对象中,
接的执行第二步
#2.处理版本信息 处理认证信息 处理权限信息 对用户的访问频率进行限制
self.initial(request, *args, **kwargs)
#2.2处理认证信息
self.perform_authentication(request)
查看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
其调用了rest_framework中Request的user方法(这个方法肯定别@property装饰,不然的话不可能直接不加括号的调用)
from rest_framework.request import Request
Request类中的user方法
@property
def user(self):
"""
Returns the user associated with the current request, as authenticated
by the authentication classes provided to the request.
"""
#判断当前类中是否有已经认证过的user
if not hasattr(self, '_user'):
#没有认证则去认证
self._authenticate()
#认证过了直接返回
return self._user
注意:user中的self代表的是request对象
没认证的话执行 调用Request类中的_authenticate()方法
def _authenticate(self):
"""
Attempt to authenticate the request using each authentication instance
in turn.
"""
#遍历request对象中封装的Authentication对象
for authenticator in self.authenticators:
try:
#调用Authentication对象中的authenticate方法,必须要有这个方法不然抛出异常
#当然Authentication类一般有我们自己定义,实现这个方法就可以了
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()
认证实例:
局部认证:直接类中定义Authentication类
from django.db import models # Create your models here.
class Userinfo(models.Model):
name=models.CharField(max_length=32,verbose_name='用户名')
pwd=models.CharField(max_length=32,verbose_name='密码')
token=models.CharField(max_length=64,null=True) def __str__(self):
return self.name
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^users/', views.HostView.as_view()),
url(r'^auth/', views.AuthView.as_view()),
]
import json
import hashlib
import time from django.shortcuts import render, HttpResponse
from rest_framework.views import APIView
from rest_framework.authentication import SessionAuthentication
from rest_framework.authentication import BaseAuthentication
from rest_framework.request import Request
from rest_framework.exceptions import APIException
from rest_framework.response import Response from app01 import models # Create your views here.
class MyAuthentication(object):
def authenticate(self, request):
token = request.query_params.get('token')
user=models.Userinfo.objects.filter(token=token).first()
if user:
return (user.name, 'aaaaaa')
raise APIException('认证失败') class AuthView(APIView):
# 设置为空标识不用认证
authentication_classes = [] def get(self, request, *args, **kwargs):
dic = {'code': 1000, 'msg': ''}
pwd = request.query_params.get('pwd')
name = request.query_params.get('name') user = models.Userinfo.objects.filter(name=name, pwd=pwd).first()
if not user:
dic['msg'] = '用户名或密码错误'
dic['code'] = 1001
return Response(dic)
t = time.time()
key = '%s|%s' % (name, t)
m = hashlib.md5()
m.update(key.encode('utf-8'))
token = m.hexdigest() user.token = token
user.save()
dic['token'] = token
dic['msg'] = '登陆成功'
return Response(dic) class HostView(APIView):
authentication_classes=[MyAuthentication,]
def dispatch(self, request, *args, **kwargs):
"""
请求到来之后,都要执行dispatch方法,dispatch方法根据请求方式不同触发 get/post/put等方法 注意:APIView中的dispatch方法有好多好多的功能
"""
return super().dispatch(request, *args, **kwargs) def get(self, request, *args, **kwargs):
return Response('GET请求,响应内容') def post(self, request, *args, **kwargs):
return Response('POST请求,响应内容') def put(self, request, *args, **kwargs): return Response('PUT请求,响应内容')
Views
全局认证;在settings中配置
from django.db import models # Create your models here.
class Userinfo(models.Model):
name=models.CharField(max_length=32,verbose_name='用户名')
pwd=models.CharField(max_length=32,verbose_name='密码')
token=models.CharField(max_length=64,null=True) def __str__(self):
return self.name
models
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^users/', views.HostView.as_view()),
url(r'^auth/', views.AuthView.as_view()),
]
urls
from rest_framework.exceptions import APIException
from rest_framework.response import Response
from rest_framework.authentication import BaseAuthentication from app01 import models class MyAuthentication(BaseAuthentication):
def authenticate(self, request):
token = request.query_params.get('token')
user=models.Userinfo.objects.filter(token=token).first()
if user:
return (user.name, 'aaaaaa')
raise APIException('认证失败')
utils
REST_FRAMEWORK = {
'UNAUTHENTICATED_USER': None,
'UNAUTHENTICATED_TOKEN': None,
"DEFAULT_AUTHENTICATION_CLASSES": [
"app01.utils.MyAuthentication",
],
}
settings中
Django rest framework 的认证流程(源码分析)的更多相关文章
- Spring Securtiy 认证流程(源码分析)
当用 Spring Security 框架进行认证时,你可能会遇到这样的问题: 你输入的用户名或密码不管是空还是错误,它的错误信息都是 Bad credentials. 那么如果你想根据不同的情况给出 ...
- Django rest framework 限制访问频率(源码分析)
基于 http://www.cnblogs.com/ctztake/p/8419059.html 当用发出请求时 首先执行dispatch函数,当执行当第二部时: #2.处理版本信息 处理认证信息 处 ...
- Django框架之DRF 认证组件源码分析、权限组件源码分析、频率组件源码分析
认证组件 权限组件 频率组件
- Django-restframework 源码之认证组件源码分析
Django-restframework 源码之认证组件源码分析 一 前言 之前在 Django-restframework 的流程分析博客中,把最重要的关于认证.权限和频率的方法找到了.该方法是 A ...
- Django REST framework —— 认证组件源码分析
我在前面的博客里已经讲过了,我们一般编写API的时候用的方式 class CoursesView(ViewSetMixin,APIView): pass 这种方式的有点是,灵活性比较大,可以根据自己的 ...
- Django框架(十七)-- CBV源码分析、restful规范、restframework框架
一.CBV源码分析 1.url层的使用CBV from app01 import views url(r'book/',views.Book.as_view) 2.as_view方法 as_view是 ...
- Django(63)drf权限源码分析与自定义权限
前言 上一篇我们分析了认证的源码,一个请求认证通过以后,第二步就是查看权限了,drf默认是允许所有用户访问 权限源码分析 源码入口:APIView.py文件下的initial方法下的check_per ...
- Django之admin的使用及源码分析
一.admin组件使用 Django本身提供了基于 web 的管理工具.其管理工具是django.contrib的一部分,可在settings.py中的 INSTALLED_APPS 看到: INST ...
- Shiro入门学习之shi.ini实现认证及源码分析(二)
一.Shiro.ini文件 1.文件说明 ①ini(InitializationFile)初始文件:Window系统文件扩展名 ②Shiro使用时可以连接数据库,也可以不连接数据库(可以使用shiro ...
随机推荐
- Redis 学习之常用命令及安全机制
该文使用centos6.5 64位 redis3.2.8 一.redis常用命令 键值常用命令: 1. keys 返回满足pattern的所有key. 127.0.0.1:6379> ke ...
- Python数据定义
数据类型: 什么是数据? 在计算机科学中,数据是指所有能输入到计算机并被计算机程序处理的符号的介质的总称,是用于输入电子计算机进行处理,具有一定意义的数字字母.符号和模拟量等的统称.现在计算机存储和处 ...
- JS作用域-面向对象
1. 其它语言是以代码块作为作用域的.下面程序会报错(如C,C++中),因为局部变量name只在{ }代码块中生效.打印console.writeline(name)中的name时就会报错. pu ...
- JS变量对象详解
JS变量对象详解 开年之后工作热情一直不是很高,这几天一直处于消极怠工状态.早上不想起床,起床了不想上班.明明放假之前工作热情还一直很高,一直心心念念的想把小程序项目怼出来,结果休假回来之后画风完全不 ...
- [51nod1325]两棵树的问题
description 题面 solution 点分治+最小割. 点分必选的重心,再在树上dfs判交,转化为最大权闭合子图. 可以做\(k\)棵树的情况. code #include<iostr ...
- spring+springMVC+mybatis较全
1.基本概念 1.1.Spring Spring是一个开源框架,Spring是于2003 年兴起的一个轻量级的Java 开发框架,由Rod Johnson 在其著作Expert One-On- ...
- BZOJ4943 & 洛谷3823 & UOJ315:[NOI2017]蚯蚓排队——题解
https://www.lydsy.com/JudgeOnline/problem.php?id=4943 http://uoj.ac/problem/315 https://www.luogu.or ...
- PE格式示意图
- 【神仙题】【P4885】 灭顶之灾
传送门 Description 请将题目名称的首字母连起来读 Scarlet有一张$n*m$的神秘表格.现在Scarlet向表格中填数字,她会从第一行中的某个格子起,按照从左往右,从上往下的顺序依次填 ...
- selenium - webdriver - 设置元素等待
隐式等待:implicitly_wait(value), value默认是0 from selenium import webdriverfrom selenium.common.exceptions ...