Django框架(二十)—— Django rest_framework-认证组件
Django rest_framework-认证组件
[TOC]
一、什么是认证
只有认证通过的用户才能访问指定的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()
Django框架(二十)—— Django rest_framework-认证组件的更多相关文章
- Django框架(十四)-- forms组件、局部钩子、全局钩子
一.什么是forms组件 forms组件就是一个类,可以检测前端传来的数据,是否合法. 例如,前端传来的邮箱数据,判断邮件格式对不对,用户名中不能以什么开头,等等 二.forms组件的使用 1.使用语 ...
- Django框架(十五)—— forms组件、局部钩子、全局钩子
目录 forms组件.局部钩子.全局钩子 一.什么是forms组件 二.forms组件的使用 1.使用语法 2.组件的参数 3.注意点 三.渲染模板 四.渲染错误信息 五.局部钩子 1.什么是局部钩子 ...
- Django框架 之 admin管理工具(组件使用)
Django框架 之 admin管理工具(组件使用) 浏览目录 激活管理工具 使用管理工具 admin的定制 admin组件使用 Django 提供了基于 web 的管理工具. Django 自动管理 ...
- 微信小程序把玩(二十八)image组件
原文:微信小程序把玩(二十八)image组件 image组件也是一个程序不可缺少的,可以这样说一个app中image组件随处可以看到,一般 image有两种加载方式第一种是网络图片第二种是本地图片资源 ...
- 微信小程序把玩(二十九)video组件
原文:微信小程序把玩(二十九)video组件 视频播放组件与图片加载组件也没啥差别,使用起来也没啥注意的 重要属性: wxml <!--监听button点击事件--> <button ...
- 微信小程序把玩(二十四)toast组件
原文:微信小程序把玩(二十四)toast组件 toast消息提示框,可用在提示一些信息,比如清楚缓存给用户一个友好的提示!或操作一些请求不想让用户有什么操作,toast也可以做到因为toast显示时其 ...
- 微信小程序把玩(二十五)loading组件
原文:微信小程序把玩(二十五)loading组件 loading通常使用在请求网络数据时的一种方式,通过hidden属性设置显示与否 主要属性: wxml <!----> <butt ...
- 微信小程序把玩(二十六)navigator组件
原文:微信小程序把玩(二十六)navigator组件 navigator跳转分为两个状态一种是关闭当前页面一种是不关闭当前页面.用redirect属性指定. 主要属性: wxml <naviga ...
- Django框架(十九)--Django rest_framework-认证组件
一.什么是认证 只有认证通过的用户才能访问指定的url地址,比如:查询课程信息,需要登录之后才能查看,没有登录,就不能查看,这时候需要用到认证组件 二.利用token记录认证过的用户 1.什么是tok ...
- Django的rest_framework认证组件之全局设置源码解析
前言: 在我的上一篇博客我介绍了一下单独为某条url设置认证,但是如果我们想对所有的url设置认证,该怎么做呢?我们这篇博客就是给大家介绍一下在Rest_framework中如何实现全局的设置认证组件 ...
随机推荐
- chromedriver安装报错
解决方法: 可以使用 npm init -f命令生成package.json,package.json中缺少的字段可以参照模板 package.json进行填充,package.json中的字段 ...
- sysenter内核入口点代码分析
参考:http://www.mouseos.com/windows/kernel/KiFastCallEntry.html http://www.mouseos.com/windows/kernel/ ...
- 字符串模式匹配算法系列(一):BF算法
算法背景: BF(Brute Force)算法,是一种在字符串匹配的算法中,比较符合人类自然思维方式的方法,即对源字符串和目标字符串逐个字符地进行比较,直到在源字符串中找到完全与目标字符串匹配的子字符 ...
- Digital Root 的推导
背景 在LeetCode上遇到这道题:Add Digits 大意是给一个数,把它各位数字相加得到一个数,如果这个数小于10就返回,不然继续 addDigits(这个相加得到的数). 题目很简单,但是如 ...
- Neo4j查询节点间最短路径
Neo4j最短路径问题 1.指定某一结点 无向边: MATCH (p1:Person {name:"aaaaaaa"}),(p2:Person{name:"bbbbbb& ...
- wireshark 识别http的标准
使用route add 本机ip 255.255.255.255 网关ip metric 1 的方式 会使访问本机ip的连接发送到局域网内,这样wireshark就可以抓取到 但有一点需要注意 如果本 ...
- 如何减少代码中的if-else嵌套
实际项目中,往往有大量的if-else语句进行各种逻辑校验,参数校验等等,大量的if-else,语句使代码变得臃肿且不好维护,本篇文章结合我自己的经验,就减少if-else语句给出以下几种方案,分别适 ...
- Nginx+Keepalived主从配置(双机主从热备)+Tomcat集群
拓扑环境 以下表格是这次測试须要的拓扑环境,几台server.每台server上安装什么,都有介绍. server名称 系统版本号 预装软件 IP地址/VIP Nginx主server CentOS ...
- Ansible--01 ansible基础 Ansible-ad- hoc
目录 自动化运维工具-Ansible基础 自动化运维的含义 Ansible 基础及安装 Ansible的架构 Ansible的执行流程 ansible配置文件 ansible Inventory(主机 ...
- Codeforces 1183H DP 计算子序列数目
题意及思路:https://blog.csdn.net/mmk27_word/article/details/93999633 第一次见这种DP,有点像退背包的思想,如果发现有可能因为字母相同和前面算 ...