Django rest_framework-认证组件

一、什么是认证

只有认证通过的用户才能访问指定的url地址,比如:查询课程信息,需要登录之后才能查看,没有登录,就不能查看,这时候需要用到认证组件

二、利用token记录认证过的用户

1、什么是token

token是服务端生成的一串字符串,以作客户端进行请求的一个令牌,当第一次登录后,服务器生成一个Token便将此Token返回给客户端,以后客户端只需带上这个Token前来请求数据即可,无需再次带上用户名和密码。session的存储是需要空间的,session的传输一般都是通过cookie来传输,或url重写的方式。
token在服务器时可以不用存储用户信息的,token传递的方式也不限于cookie传递,token也可以保存起来

2、token的原理

当第一次登录认证过后,就会返回一个token到前台,前台之后发送请求,就会带上这个token字符串,

3、cookie、session、token的区别

cookie是保存在浏览器,以key:value的形式传递到服务器认证用户,用户名密码可能裸露,不安全
session是保存在服务器,产生随机字符串与用户信息对应,将随机字符串放在cookie中。服务器会保存一份,可能保存到缓存/数据库/文件。
token发送请求的对象可以是浏览器,也可以是移动端。服务器不需要记录任何东西,每次都是一个无状态的请求,每次都是通过解密来验证是否合法

三、drf的认证组件

# 用户信息表
class UserInfo(models.Model): name = models.CharField(max_length=32) pwd = models.CharField(max_length=32) # 用户token class UserToken(models.Model): token = models.CharField(max_length=64) user = models.OneToOneField(to=UserInfo)

1、基本使用

from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import APIException from app01 import models # 定义一个认证类 class LoginAuth(BaseAuthentication): # 重写authenticate方法 def authenticate(self, request): # 从路由中获取token,从request对象中获取,如果token放在header中 # query_params 是Request封装的原生request.GET token = request.query_params.get('token') ret = models.UserToken.objects.filter(token=token).first() if ret: # 能查到,说明认证通过,返回空,或者返回当前的用户对象 # ret.user就是当前登录用户对象,一旦retrun了,后面的认证类都不执行了 return ret.user,ret # 如果查不到,抛出异常 raise APIException('用户认证失败') 
# view 层
from rest_framework.views import APIView
# 获取随机字符串——token def get_token(name): md = hashlib.md5() md.update(str(name).encode('utf-8')) return md.hexdigest() # 用户登录 class Login(APIView): def post(self, request, *args, **kwargs): response = {'status': 100, 'msg': '登录成功'} name = request.data.get('name') pwd = request.data.get('pwd') ret = models.UserInfo.objects.filter(name=name, pwd=pwd).first() if ret: token = get_token(name) # 一旦用户信息校验通过,就产生一个token保存在Token表中 models.UserToken.objects.create(token=token, user=ret) response['token'] = token else: response['status'] = 101 response['msg'] = '用户名或密码错误' return JsonResponse(response, safe=False) # 查看所有图书信息 class Book(APIView): # 指定authentication_classes,会循环列表实例化,进行认证 # 该方法是局部使用认证 authentication_classes = [UserAuth.UserAuth, ] def get(self, request, *args, **kwargs): response = {'status': 100, 'msg': '查询成功'} ret = models.Book.objects.all() ser = MySerializer.BookSerializer(instance=ret, many=True) response['data'] = ser.data return JsonResponse(response, safe=False) 

2、全局使用、局部使用、局部禁用认证

(1)全局使用

  • 在settings文件中配置,配置完以后,就无需在视图类中写,已经是所有视图类都需要认证
  • 必须为REST_FRAMEWORK,key值必须为DEFAULT_AUTHENTICATION_CLASSES
REST_FRAMEWORK={'DEFAULT_AUTHENTICATION_CLASSES':['app01.MyAuth.LoginAuth',],
}

(2)局部使用

在需要使用的视图类中写,只对当前视图类起认证作用,重新定义authentication_classes

class Book(APIView): # 该方法是局部使用认证 authentication_classes = [UserAuth.UserAuth, ] def get(self, request, *args, **kwargs): response = {'status': 100, 'msg': '查询成功'} ret = models.Book.objects.all() ser = MySerializer.BookSerializer(instance=ret, many=True) response['data'] = ser.data return JsonResponse(response, safe=False)

(3)局部禁用

在配置过全局认证以后,有些视图类不需要认证,可以局部禁用认证,只需将authentication_classes定义为空列表即可。例如:登录视图,不应该有认证,就可以局部禁用

# settings中
REST_FRAMEWORK={'DEFAULT_AUTHENTICATION_CLASSES':['app01.MyAuth.LoginAuth',],
}# view的视图类中 class Book(APIView): # 该方法是局部使用认证 authentication_classes = [] def get(self, request, *args, **kwargs): response = {'status': 100, 'msg': '查询成功'} ret = models.Book.objects.all() ser = MySerializer.BookSerializer(instance=ret, many=True) response['data'] = ser.data return JsonResponse(response, safe=False)

3、不存数据库的token实现认证

# 登录以后,会产生一个随机字符串,返回到移动端/PC端,下一次发送请求,就在后面拼上  ?token=asdfasdgasdg|{"id":1},这样只要将后面的id通过加密获取随机字符串,与原字符串比较是否一致即可,不用再讲token存到数据库
def check_token(token): user = None ret = True try: # token 拿到的是 ‘fsdfasdfasd|{\"name\": \"lqz\", \"id\": 1}’ ll = token.split('|') md = haslib.md5() md.update(ll[1].encode('utf-8')) md5.update(settings.password.encode('utf-8')) hex = md.hexdigest() if hex == ll[0]: user = ll[1] else: ret = False except Exception as e: ret = False return ret,user class LoginAuth(BaseAuthentication): # 重写authenticate方法 def authenticate(self, request): token = request.query_params.get('token') ret, user_info = check_token(token) if ret: return user_info, None # 如果查不到,抛异常 raise exceptions.APIException('您认证失败') 
def create_token(user_id): md5 = hashlib.md5() md5.update(user_id.encode('utf-8')) # 加盐加密 md5.update(settings.password.encode('utf-8')) hex = md5.hexdigest() # 加密完以后,直接在后面拼上id,用于认证,传过来的id加密以后是否和原token一致 token = hex + '|' + user_id print(token) return token # 登录 # 产生随机字符串的时候就不需要存到数据库中 class Login(APIView): authentication_classes = [] def post(self, request, *args, **kwargs): response = {'status': 100, 'msg': '登录成功'} name = request.data.get('name') pwd = request.data.get('pwd') try: user = models.UserInfo.objects.get(name=name, pwd=pwd) user_info_json = json.dumps({'name': user.name, 'id': user.pk}) # 生产dafgasdewf|{'name':user.name,'id':user.pk}的token token = create_token(str(user.pk)) response['token'] = token except ObjectDoesNotExist as e: response['status'] = 101 response['msg'] = '用户名或密码错误' except Exception as e: response['status'] = 102 # response['msg']='未知错误' response['msg'] = str(e) return JsonResponse(response, safe=False)

四、源码分析

as_view ----------> view -------------> dispatch -------> Request包装新的request ------> 认证、权限、频率 --------> 根据请求方式分发到不同的方法

url(r'books/',views.Book.as_view())

1、Book中没有as_view

2、APIView的as_view

class APIView(View):  @classmethod # cls 是 Book类 def as_view(cls, **initkwargs): # view = super(APIView, Book).as_view(**initkwargs) 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、view = super(APIView, cls).as_view(**initkwargs) ---------------------> View中的as_view

class View(object):  @classonlymethod # cls====> Book def as_view(cls, **initkwargs): def view(request, *args, **kwargs): # 实例化产生一个book对象 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 view

4、return self.dispatch(request, *args, **kwargs) ----------------> dispatch

self====> Book对象,一层层找dispatch

APIView中找到dispatch

class APIView(View): def dispatch(self, request, *args, **kwargs): self.args = args self.kwargs = kwargs # (a)初始化request,就是通过Request类来包装原生request,得到包装后的request request = self.initialize_request(request, *args, **kwargs) # 从现在开始request就是包装后的request self.request = request self.headers = self.default_response_headers # deprecate? try: # (b) 认证、权限、频率 self.initial(request, *args, **kwargs) # Get the appropriate handler method # 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) self.response = self.finalize_response(request, response, *args, **kwargs) return self.response 

(a)request = self.initialize_request(request, *args, **kwargs) 包装 request

self 是Book对象

class APIView(View): # 默认的认证列表类 authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES def initialize_request(self, request, *args, **kwargs): """ Returns the initial request object. """ parser_context = self.get_parser_context(request) # (a-b)实例化初始化产生新的request对象 return Request( request, parsers=self.get_parsers(), authenticators=self.get_authenticators(), # 认证类实例化产生的对象的列表 negotiator=self.get_content_negotiator(), parser_context=parser_context )
    def get_authenticators(self): """ Instantiates and returns the list of authenticators that this view can use. """ return [auth() for auth in self.authentication_classes]
(a------1)return Request( ··· ) ----------> Request类初始化
    def __init__(self, request, parsers=None, authenticators=None, negotiator=None, parser_context=None): assert isinstance(request, HttpRequest), ( 'The `request` argument must be an instance of ' '`django.http.HttpRequest`, not `{}.{}`.' .format(request.__class__.__module__, request.__class__.__name__) ) self._request = request self.parsers = parsers or () self.authenticators = authenticators or () self.negotiator = negotiator or self._default_negotiator() self.parser_context = parser_context self._data = Empty self._files = Empty self._full_data = Empty self._content_type = Empty self._stream = Empty if self.parser_context is None: self.parser_context = {} self.parser_context['request'] = self self.parser_context['encoding'] = request.encoding or settings.DEFAULT_CHARSET force_user = getattr(request, '_force_auth_user', None) force_token = getattr(request, '_force_auth_token', None) if force_user is not None or force_token is not None: forced_auth = ForcedAuthentication(force_user, force_token) self.authenticators = (forced_auth,)

(b)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. version, scheme = self.determine_version(request, *args, **kwargs) request.version, request.versioning_scheme = version, scheme # Ensure that the incoming request is permitted # (b------1) 认证 self.perform_authentication(request) # (b------2)权限 self.check_permissions(request) # 频率 self.check_throttles(request)
(b------1) self.perform_authentication(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

(b------1------1) 调用request的user方法,request是Request实例化产生的对象

class Request(object):  @property def user(self): # 这里的self是request if not hasattr(self, '_user'): with wrap_attributeerrors(): self._authenticate() # 具体方法如下 return self._user

(b------1------1-------1) self._authenticate()

class Request(object): def _authenticate(self): # 这里的self是request # self.authenticators就是在Request实例化的时候,传过来的认证类的对象的列表 # 即:[auth() for auth in self.authentication_classes] for authenticator in self.authenticators: try: # 重写的就是这个authenticate()方法, # 两个参数,第一个是对象本身(自动传);第二个是这里的self,就是request 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()

博客内容仅供参考,部分参考他人优秀博文,仅供学习使用

转载于:https://www.cnblogs.com/zhuzhiwei-2019/p/10779203.html

Django框架(二十)—— Django rest_framework-认证组件相关推荐

  1. 微信小程序把玩(二十八)image组件

    原文:微信小程序把玩(二十八)image组件 image组件也是一个程序不可缺少的,可以这样说一个app中image组件随处可以看到,一般 image有两种加载方式第一种是网络图片第二种是本地图片资源 ...

  2. 微信小程序把玩(二十六)navigator组件

    微信小程序把玩(二十六)navigator组件 原文:微信小程序把玩(二十六)navigator组件 navigator跳转分为两个状态一种是关闭当前页面一种是不关闭当前页面.用redirect属性指 ...

  3. Django框架(十八)—— auth框架:用户登录、注册、认证

    auth模块 一.什么是author模块 Auth模块是Django自带的用户认证模块,可以实现包括用户注册.用户登录.用户认证.注销.修改密码等功能.默认使用 auth_user 表来存储用户数据. ...

  4. python Django web 框架 (二十)之ORM

    Django之模型层第一篇:单表操作 一 ORM简介 我们在使用Django框架开发web应用的过程中,不可避免地会涉及到数据的管理操作(如增.删.改.查),而一旦谈到数据的管理操作,就需要用到数据库 ...

  5. Django 框架篇: 一. Django介绍; 二. 安装; 三. 创建项目;

    Django介绍 一. web框架:   框架,即framework,特指为解决一个开放性问题而设计的具有一定约束性的支撑结构,使用框架可以帮你快速开发特定的系统,简单地说,就是你用别人搭建好的舞台来 ...

  6. django框架——sweetalert前端插件、序列化组件、批量数据操作、分页器、Forms组件(上)

    系列文章目录 第一章 django安装与介绍 第二章 django基础使用 第三章 路由层 第四章 虚拟环境.django版本区别.视图层 第五章 模板层 第六章 模型层(上) 第七章 模型层(下) ...

  7. Django框架(二)

    一:Django项目创建步骤: 方式1:命令创建: 进入指定目录 C:\Users\bing>F: F:\>cd mysite F:\mysite>django-admin star ...

  8. Python Web开发框架之Django篇——二、Django连接MySQL数据库以及建表的操作

    二.Django连接MySQL数据库以及建表的操作 准备工作:安装Python访问MySQL的模块 一.修改project同名目录下面的__init__.py文件 二.修改project同名目录下面的 ...

  9. Django框架(十九)—— drf:序列化组件(serializer)

    序列化组件 # 模型层 from django.db import modelsclass Book(models.Model): nid = models.AutoField(primary_key ...

  10. Django框架(十二)-- Djang与Ajax

    一.什么是Ajax AJAX(Asynchronous Javascript And XML)翻译成中文就是"异步Javascript和XML".即使用Javascript语言与服 ...

最新文章

  1. Vitamio FAQ(2012-11-20 )
  2. 数字转换成字符串进行连接
  3. Eclipse出现Class Not Found异常时可以参考我所知道的一个方案
  4. 为什么有的S4系统看不到Adapt UI按钮
  5. Linux网络编程一步一步学-select详解
  6. leetcode78 子集
  7. 分账和无协议商户数据有误的处理过程
  8. Cisco路由器安全配置方案
  9. vue 第九天 v-model的基本使用
  10. c 是高级程序设计语言吗,下列各类计算机程序语言中,不属于高级程序设计语言的是()。A.Visual BasicB.FORTRAN语言C.Pasca...
  11. swfobject 2.0 使用
  12. Java:实现动态规划的4个经典题型,你都会吗?拒绝做优秀的码农
  13. TreeUtil(树形结构转换)
  14. android共享局域网打印,Android 浅谈同一局域网下使用WiFi连接打印机
  15. matlab画微分方程的矢量场图_一维波动方程数值解 Matlab 教程(从入门到出图)——3数值计算的Matlab实现...
  16. 关于人工智能AI的发展,边缘计算中的AI芯片
  17. 如何过滤好的和坏的价格交易信号
  18. 迅为RK3568开发板Debian系统安装ToDesk
  19. 2022第三届全国大学生网络安全精英赛练习题(2)
  20. C语言中的cbp文件是什么,C语言编程入门——函数(上)

热门文章

  1. 移动端调取摄像头上面如何给出框_飞桨实战笔记:自编写模型如何在服务器和移动端部署...
  2. keil5函数 默认返回值_C++的返回值return
  3. xml文件上传服务器读取不了,本地读取服务器Xml文件及本地读本地的xml
  4. Java自带的广告怎么删掉_如何屏蔽电脑上的弹窗广告?
  5. 厉害了!java多线程导出excel
  6. 【机器学习】树回归和聚类算法解析和应用
  7. python【数据结构与算法】棋盘覆盖问题
  8. python【数据结构与算法】最小生成树之Kruskal算法
  9. python【蓝桥杯vip练习题库】BASIC-3字母图形
  10. matlab积分与绘图