Django(62)自定义认证类
前言
如果我们不用使用drf
那套认证规则,我们想自定义认证类,那么我们首先要知道,drf
本身是如何定义认证规则的,也就是要查看它的源码是如何写的
源码分析
源码的入口在APIView.py
文件下的dispatch
方法下的self.initial
方法中的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
返回了一个request
的user
方法,request
代表的是drf
的Request
,所以我们进入drf
的Request
类中查找user
方法属性,源码如下:
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'):
with wrap_attributeerrors():
self._authenticate()
return self._user
上述代码的意思是:返回与当前请求关联的用户,由提供给请求的身份验证类进行身份验证。如果没有用户,我们需要通过_authenticate
方法验证,我们查看下它的源码
def _authenticate(self):
"""
尝试依次使用每个身份验证实例对请求进行身份验证。
"""
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
验证器其实是调用父类APIView
的authenticators
,APIView
的authenticators
在源码initialize_request
方法下的get_authenticators
,我们查看源码
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_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES
我们就知道了drf
默认的认证器在settings
文件下的DEFAULT_AUTHENTICATION_CLASSES
类下面
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.BasicAuthentication'
],
我们发现drf
默认有2个认证类一个基础的认证,另一个session
认证,这两个认证类都继承自BaseAuthentication
,我们来看下源码
class BaseAuthentication:
"""
所有的认证类都继承自BaseAuthentication.
"""
def authenticate(self, request):
"""
认证请求返回一个二元组(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
接下来我们看下BasicAuthentication
如何写的,后续我们依葫芦画瓢
class BasicAuthentication(BaseAuthentication):
"""
针对用户名密码的 HTTP 基本身份验证
"""
www_authenticate_realm = 'api'
def authenticate(self, request):
"""
如果使用 HTTP 基本身份验证提供了正确的用户名和密码,则返回“User”。否则返回“None”。
"""
# 获取请求头中`HTTP_AUTHORIZATION`,并进行分割
auth = get_authorization_header(request).split()
# 如果没有auth或者auth的第一个索引值的小写不等于basic,则返回None
if not auth or auth[0].lower() != b'basic':
return None
# auth列表的长度必须等于2,格式['basic', 'abc.xyz.123']
# 如果auth的长度等于1,则抛出异常
if len(auth) == 1:
msg = _('Invalid basic header. No credentials provided.')
raise exceptions.AuthenticationFailed(msg)
# 如果长度大于2,也抛出异常
elif len(auth) > 2:
msg = _('Invalid basic header. Credentials string should not contain spaces.')
raise exceptions.AuthenticationFailed(msg)
try:
try:
# auth[1]解码格式为utf-8
auth_decoded = base64.b64decode(auth[1]).decode('utf-8')
except UnicodeDecodeError:
auth_decoded = base64.b64decode(auth[1]).decode('latin-1')
auth_parts = auth_decoded.partition(':')
except (TypeError, UnicodeDecodeError, binascii.Error):
msg = _('Invalid basic header. Credentials not correctly base64 encoded.')
raise exceptions.AuthenticationFailed(msg)
userid, password = auth_parts[0], auth_parts[2]
return self.authenticate_credentials(userid, password, request)
def authenticate_credentials(self, userid, password, request=None):
"""
Authenticate the userid and password against username and password
with optional request for context.
"""
credentials = {
get_user_model().USERNAME_FIELD: userid,
'password': password
}
user = authenticate(request=request, **credentials)
if user is None:
raise exceptions.AuthenticationFailed(_('Invalid username/password.'))
if not user.is_active:
raise exceptions.AuthenticationFailed(_('User inactive or deleted.'))
return (user, None)
def authenticate_header(self, request):
return 'Basic realm="%s"' % self.www_authenticate_realm
自定义认证类
- 创建继承BaseAuthentication的认证类
- 实现authenticate方法
- 实现体根据认证规则 确定 游客 正常用户 非法用户
- 进行全局或局部配置(一般采用全局配置)
认证规则
- 没有认证信息,返回
None
(游客) - 有认证信息认证失败,抛异常(非法用户)
- 有认证信息认证成功,返回用户和认证信息的元组(合法用户)
我们创建一个文件夹authentications
,写入如下代码
from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
from api.models import User
class MyAuthentications(BaseAuthentication):
def authenticate(self, request):
# 前台在请求头携带认证信息
# 且默认规范用Authorization字段携带认证信息
# 后台固定在请求对象的META字段中的HTTP_AUTHORIZATION获取
auth = request.META.get('HTTP_AUTHORIZATION', None)
# 处理游客
if auth is None:
return None
auth_list = auth.split()
if not len(auth_list) == 2 and auth_list[0].lower() == "auth":
raise AuthenticationFailed("认证信息有误,非法用户")
# 合法的用户还需要从auth_list[1]中解析出来
# 注:假设一种情况,信息为xx.yy.zz,就可以解析出admin用户:实际开发,该逻辑一定是校验用户的正常逻辑
if auth_list[1] != 'xx.yy.zz': # 校验失败
raise AuthenticationFailed("用户校验失败,非法用户")
user = User.objects.filter(username='jkc').first()
print(user)
if not user:
raise AuthenticationFailed("用户数据有误,非法用户")
return user, None
然后在settings.py
中配置全局的自定义认证类
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'api.authentications.MyAuthentications'
],
}
最后写入视图函数
class TestView(APIView):
def get(self, request, *args, **kwargs):
return APIResponse(data_msg="drf get ok")
然后我们访问视图,在headers
中不传Authorization
代表游客,游客可以访问成功
{
"statusCode": 0,
"message": "drf get ok"
}
接着我们在请求头中只传auth
访问视图会抛出异常信息
{
"detail": "认证信息有误,非法用户"
}
然后我们在请求头中传入错误的认证,auth 111
访问视图会抛出异常信息
{
"detail": "用户校验失败,非法用户"
}
最后我们在请求头中传入正确的认证,auth xx.yy.zz
,这次会得到正确的返回结果
{
"statusCode": 0,
"message": "drf get ok"
}
以上的测试,就代表我们自定义的认证类起作用了
Django(62)自定义认证类的更多相关文章
- DRF JWT的用法 & Django的自定义认证类 & DRF 缓存
JWT 相关信息可参考: https://www.jianshu.com/p/576dbf44b2ae DRF JWT 的使用方法: 1. 安装 DRF JWT # pip install djang ...
- 自定义user表签发token、自定义认证类、simpleui模块使用
今日内容概要 自定义User表,签发token 自定义认证类 simpleui的使用 多方式登陆接口(后面也写 内容详细 1.自定义User表,签发token # 如果项目中的User表使用auth的 ...
- [py][mx]django自定义认证类-实现邮箱作为用户名登录
创建自定义验证用户名密码类CustomBackend users/views.py from django.contrib.auth import authenticate, login from d ...
- django 实现自定义认证
1.Django自带用户认证系统 Django自带用户认证系统,这个系统支持访问控制.注册用户.关联创建者和内容等:在开发用户认证功能时的时候,可以使用Django自带用户认证系统实现: A.相关表 ...
- The Django Book(自定义ModelAdmi类)
默认的,管理界面下显示的东西只是 python2:def __unicode__(self): 和 python3:def __str__(self): 中返回的字段内容 想要让它更加的多元化的话 c ...
- Django:RestFramework之-------认证
3 restframework-认证 3.1APIView 认证: 认证是否已经登陆,如果已经登陆返回元组,如果没有登陆报错 源码流程: 执行dispatch方法: def dispatch(self ...
- Django REST framework —— 认证组件源码分析
我在前面的博客里已经讲过了,我们一般编写API的时候用的方式 class CoursesView(ViewSetMixin,APIView): pass 这种方式的有点是,灵活性比较大,可以根据自己的 ...
- drf三大认证:认证组件-权限组件-权限六表-自定义认证组件的使用
三大认证工作原理简介 认证.权限.频率 源码分析: from rest_framework.views import APIView 源码分析入口: 内部的三大认证方法封装: 三大组件的原理分析: 权 ...
- DRF认证、自定义认证和权限、自定义权限
源码分析 """ 1)APIView的dispath(self, request, *args, **kwargs) 2)dispath方法内 self.initial( ...
随机推荐
- SSRF_FastCGI
SSRF_FastCGI 目录 SSRF_FastCGI FastCGI协议 SSRF ssrf + fastcgi 参考 FastCGI协议 简介 Fast CGI源自旧版本的CGI 路由/结构图 ...
- 【python】Leetcode每日一题-逆波兰表达式求值
[python]Leetcode每日一题-逆波兰表达式求值 [题目描述] 根据 逆波兰表示法,求表达式的值. 有效的算符包括 +.-.*./ .每个运算对象可以是整数,也可以是另一个逆波兰表达式. 说 ...
- jupyter中那些神奇的第三方拓展魔术命令
1 简介 无论是jupyter notebook还是jupyter lab,都可以使用ipython中的众多自带魔术命令来实现丰富的辅助功能,诸如%time之类的. 这些都已经是老生常谈的知识没什么好 ...
- opencv——图像遍历以及像素操作
摘要 我们在图像处理时经常会用到遍历图像像素点的方式,在OpenCV中一般有四种图像遍历的方式,在这里我们通过像素变换的点操作来实现对图像亮度和对比度的调整. 补充: 图像变换可以看成 像素变换--点 ...
- (数据科学学习手札121)Python+Dash快速web应用开发——项目结构篇
本文示例代码已上传至我的Github仓库https://github.com/CNFeffery/DataScienceStudyNotes 1 简介 这是我的系列教程Python+Dash快速web ...
- python爬虫——抖音数据
最近挺火的抖音短视频,不仅带火了一众主播,连不少做电商的也进驻其中,于是今天我来扒一扒这火的不要不要的抖音数据: 一.抓包工具获取用户ID 对于手机app数据,抓包是最直接也是最常见的手段,常用的抓包 ...
- 前端必读:Vue响应式系统大PK
转载请注明出处:葡萄城官网,葡萄城为开发者提供专业的开发工具.解决方案和服务,赋能开发者. 原文参考:https://www.sitepoint.com/vue-3-reactivity-system ...
- Java_封装
分类(分层)思想 dao层(数据访问层):对数据进行管理的操作(增.删.改.查). 数据库.数组.集合 service层(业务层): 具体做一些业务操作 controller(控制层): 用来接收用户 ...
- [bug] MySQL-Front连接MySQL 8.0失败
原因: MySQL-Front不支持MySQL 8.0的密码认证方式 解决: 在mysql安装目录中my.ini文件末尾添加 default_authentication_plugin=mysql_n ...
- python实现UDP通讯
Environment Client:Windows Server:KaLi Linux(VM_virtul) Network:Same LAN Client UDPClient.py #-*- co ...