1. 认证之APIView

  在聊APIView之前, 我们先重温一下django2.x的CBV流程

  a. 对于django而言, 当浏览器请求到达之后,按照规则首先会经过各大中间件(Middleware)(process_request, process_view, process_response), 而与CBV挂钩的便是各中间件的process_view方法,以CSRFViewMiddleware为例,django会默认开启全局CSRF验证,而在实际开发中,一部分视图处理类是不需要CSRF验证的,所以我们需要在处理类方法上加上@method_decorator(csrf_exempt)装饰器来取消验证,下面是CSRFViewMiddleware类下的process_view的部分源码,

    def process_view(self, request, callback, callback_args, callback_kwargs):
if getattr(request, 'csrf_processing_done', False):
return None # Wait until request.META["CSRF_COOKIE"] has been manipulated before
# bailing out, so that get_token still works
if getattr(callback, 'csrf_exempt', False):
return None

  b.为什么执行了视图类的as_view(), 便能够处理各种类型(GET, POST, DELETE, HEAD等等)请求呢? 我们知道,视图类是利用dispatch(self, *args, **kwargs)的反射来实现请求(GET/POST......)与处理方法( get(self, request) / post(self, request) )之间的映射,所以实际上as_view()方法是调用了视图类中的dispatch来实现调用具体的请求处理方法的。下面是as_view()的部分源码:

     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

  

  了解了CBV, 那APIView又是怎么一回事?

  a. APIView是View的子类,其处理请求流程与View一致,但APiView内部对原生request对象进行了封装,下面是其封装request的部分源码

可以看出原生request对象被赋到新建request对象的_request属性中,接下来便是基于APIView的认证了,它又是如何实现的呢?

2.基于restframework实现认证

  在前后端分离的项目中, 常常需要判断用户是否登陆, 其登录状态的维持需要借助于令牌(Token),如果在不整合restframework框架的情况下,我们要实现这一功能可以自定义一个校验装饰器来装饰每一个需要登陆之后才能执行的试图函数或者视图类方法,而在restframework中,它变得更加灵活与规范, 具体体现在APIView提供支持多重验证,验证与视图函数较低的耦合度,支持全局配置验证规则等等,下面我们从APIView的源码中详细的了解这一过程:

  1. 从第一部分对apiview的分析中, 我们可以知道, APIView新构建了一个request,该request对象不仅封装了原生request对象的所有属性(_request),而且扩展了很多针对验证,访问频率限制,权限控制等等新的特性;

  2. 就认证(authentication)而言,

      

        那么子类(基类为APIView)的authentication_classes属性为自定义的验证规则,那他又是如何执行这一规则的呢? 从第一部分的APIView创建新的request对象中我们知道,其request对象的authenticators属性为get_authenticators()方法的返回值,那么进入get_authenticators():

   

     怎么定义一个处理用户认证的类呢?答案位于rest_framework.authentication下的BaseAuthentication。 需要说明的是,继承该类的认证类必须拥有其authenticate(self, request, )以及authenticate_header(self, request)方法

   欧克,到这里的话, 我们来实战一下

   我们在名为testrestful中创建两张表,一张存用户基本信息,一张存用户token, 并在项目根目录下执行python manage.py makemigrations、python manage.py migrate命令,并手动添加一些用户数据到UserProfile

# models.py (testrestful)

from django.db import models
from django.contrib.auth.models import User
# Create your models here. class UserProfile(models.Model):
# user = models.OneToOneField(to='User', on_delete=models.CASCADE)
username = models.CharField(max_length=32, unique=True, null=False, blank=False)
password = models.CharField(max_length=32, unique=False, null=False, blank=False)
level_choice = (
(1, '普通用户'),
(2, 'VIP'),
(3, 'SVIP'),
)
level = models.SmallIntegerField(choices=level_choice, null=False, blank=False)
def __str__(self):
return '[model: %s]' % self.username class UserToken(models.Model):
user = models.OneToOneField(to='UserProfile',on_delete=models.CASCADE)
token = models.CharField(max_length=64)
expired = models.DateTimeField(null=True)

  在testrestful目录下新建utils包,包下新建auth.py(也可以直接在views.py中创建, )

  其流程是: 已注册的用户发起登陆请求  ---->  认证处理 (用户名及密码正确则派发新的Token) ------>   用户保存token 后续请求附上token得到相应数据

from rest_framework import exceptions
from rest_framework.authentication import BasicAuthentication, BaseAuthentication
from testrestful import models
import hashlib
import uuid def md5Token(user: models.UserProfile)->str:
"""用于生成token"""
# 用户名唯一, 并将其作为md5对象的salt
hash = hashlib.md5(user.username.encode('utf-8'))
cur_data = bytes(str(uuid.uuid1()), encoding='utf-8')
hash.update(cur_data)
return hash.hexdigest() class UserAuthentication(BaseAuthentication):
"""用于认证token"""
def authenticate(self, request):
token = request._request.GET.get('token', None)
u_token_obj = models.UserToken.objects.get(token=token)
if not u_token_obj:
raise exceptions.AuthenticationFailed('用户身份验证失败!')
else:
# resframework 会将这两个元组返回给request, 以便后续操作
return (u_token_obj.user, u_token_obj)
def authenticate_header(self, request):
"""当认证失败的时候, 返回给浏览器的响应头"""
pass

   如果用户通过认证, 则以(key1, key2) 作为执行返回值,并装载到request对象中,key1对应request.user,key2对应request.auth

   

   

   那么对应的视图类就很好办了,只需要添加验证规则类就欧克

from django.http.response import JsonResponse
from rest_framework.response import Response
from rest_framework.views import APIView
from testrestful import models
from testrestful.utils.auth import UserAuthentication, md5Token class Login(APIView):
"""用于接收登录请求,派发或更新token"""
# 已经在配置中配置了全局认证类, 而匿名用户不需要认证
authentication_classes = []
msg = dict()
def post(self, request):
# 此时用户没有登录,则返回默认匿名用户, 这一用户同样能够实现自定义
print('login_user:\t', request.user)
username = request._request.GET.get('username', None)
password = request._request.GET.get('password', None)
try:
user = models.UserProfile.objects.filter(username=username, password=password).first()
# 派发token
token = md5Token(user)
# token存在则更新, 不存在则创建
models.UserToken.objects.update_or_create(user=user, defaults={'token': token})
self.msg['flag'] = 1
self.msg['content'] = token
print(self.msg)
except Exception as e:
print(e)
self.msg['flag'] = 0
self.msg['content'] = "用户名或密码错误"
return JsonResponse(self.msg) # Create your views here.
class UserInfo(APIView):
"""用于获取用户信息"""
authentication_classes = [UserAuthentication,]
def dispatch(self, request, *args, **kwargs):
return super(UserInfo, self).dispatch(request, *args, **kwargs)
def get(self, request, *args, **kwargs):
# 在UserAuthentication中的authenticate方法返回的元组被添加到了request对象中,元组第一个对象就是request.user, 第二个就是request.auth
print(request.user)
print(request.auth)
return Response("ok")
def post(self, request):
return Response("POST Response")
def delete(self, request):
return Response("DELETE Response")

   下面以postman做测试:

   a. 登陆

   

   b. 获取用户信息(需登陆)

   

3. 验证规则全局配置

  

REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': ['testrestful.utils.auth.UserAuthentication', ],
'UNAUTHENTICATED_USER': lambda : '匿名用户(request.user)',
'UNAUTHENTICATED_TOKEN': lambda : '匿名用户(request.auth)',
}

  配置完成之后, 与CSRF相似, 需要用户认证的试图类就不需要authentication_classes这一行了,不需要用户认证的匿名用户视图类覆盖authentication_class就欧克

  拿本例举例:

  

  


  

restframework框架之认证的更多相关文章

  1. Django Rest framework 框架之认证使用和源码执行流程

    用这个框架需要先安装: pip3 install djangorestframework 如果写了一个CBV的东西,继承了View. # 继承Django里面View class APIView(Vi ...

  2. Django rest-framework框架十大功能分析

    rest-framework框架有哪些作用? 一共有十点. 路由 - 可以通过as_view传参数,根据请求方式不同执行相应的方法 - 可以在url中设置一个结尾,类似于: .json 视图 - 帮助 ...

  3. restful规范和restframework框架

    什么是接口? 接口可以理解为url就是接口. 那么在其他语言里面接口也可以是约束类 restful规范是什么? RESTful是目前最流行的一种互联网软件架构.它结构清晰.符合标准.易于理解.扩展方便 ...

  4. 转载:rest-framework框架的基本组件

    知识预览 快速实例 序列化 视图三部曲 认证与权限组件 解析器 分页 回到顶部 快速实例 Quickstart 回到顶部 序列化 创建一个序列化类 简单使用 开发我们的Web API的第一件事是为我们 ...

  5. rest-framework框架

    rest-framework框架是Django里面非常重要的框架,但提到rest-framework框架就不得不说两种请求方式,那就是CBV和FBV. FBV(function base views) ...

  6. JAVAEE——BOS物流项目10:权限概述、常见的权限控制方式、apache shiro框架简介、基于shiro框架进行认证操作

    1 学习计划 1.演示权限demo 2.权限概述 n 认证 n 授权 3.常见的权限控制方式 n url拦截权限控制 n 方法注解权限控制 4.创建权限数据模型 n 权限表 n 角色表 n 用户表 n ...

  7. DRF框架之认证组件用法(第四天)

    1. 什么是drf 框架的认证组件: auth 就等于是jango中的Auth模块,Auth是自带session信息,但是 drf的认证组件可以自定义token携带过去,去判断用的 2.如何实现认证呢 ...

  8. django使用RestFramework的Token认证

    今天实现的想法有点不正规: Django Rest framework的框架的认证,API都运行良好. 现在是要自己写一个function来实现用户的功能. 而不是用Rest 框架里的APIVIEW这 ...

  9. 1、 Shiro框架:认证,授权(验权 2. Shiro框架实现权限控制方式:

    1. Shiro框架:认证,授权(验权) a) 认证逻辑:applicationCode—>通过工具类获取subject对象,调用login方法参数令牌信息->安全管理器------> ...

随机推荐

  1. thinkphp数据库查重方法

    $test_data = M('hot'); //实例化数据表 $data = $test_data->Distinct(true)->field('descriprion')->o ...

  2. python 37条编程技巧-汇总(转载+整理)

    1.原地交换两个数字 x, y =10, 20 print x, y y, x = x, y print x, y 10 20 20 10 2.链状比较操作符 n = 10 print 1 < ...

  3. 在myeclipse中maven遇见的问题

    An internal error occurred during: "Retrieving archetypes:". Java heap space 表示你的myeclipse ...

  4. [CTSC2000]丘比特的烦恼

    Description 随着社会的不断发展,人与人之间的感情越来越功利化.最近,爱神丘比特发现,爱情也已不再是完全纯洁的了.这使得丘比特很是苦恼,他越来越难找到合适的男女,并向他们射去丘比特之箭.于是 ...

  5. 51nod 1100 斜率最大

    可以用三个点简单证明斜率最大的直线两个点! #include <bits/stdc++.h> #define MAXN 10010 using namespace std; struct ...

  6. linux的SHELL编程

    管道 | 特殊的重定向 前一个命令的输出作为后一个命令的输入; 管道连接的命令数没有限制; who|wc−l统计用户数ps |sort|more 按序显示当前进程名 字符:具有特定作用的特殊字符 ,& ...

  7. AJPFX关于modifier总结

    修饰符总结 Modifiers        函数修饰符始终在返回值类型之前!!!        变量修饰符始终在变量类型之前!!!---------------------------------- ...

  8. Unity中,保存在OnInspectorGUI中改变的值

    using UnityEngine; using System.Collections; using UnityEditor; [CustomEditor( typeof( MessageLog ) ...

  9. Android图片压缩,不失真,上线项目

    当然了,图片压缩是利用了libjpeg库的基础上,牛逼的同学可以自行生成so.jar.在此给出一个链接: http://www.cnblogs.com/hrlnw/p/4403334.html 在生成 ...

  10. scala如何在任意方法中打印当前线程栈信息(StackTrace)

    1.以wordcount为例 package org.apache.spark.examples import org.apache.spark.{SparkConf, SparkContext} / ...