DRF 三大认证的配置及使用方法
三大认证
一、身份认证
1、身份认证配置
1.1 全局配置身份认证模块
身份认证组件一般都配置在全局settings中。
# settings.py
# drf框架自定义配置
REST_FRAMEWORK = {
# 认证组件
'DEFAULT_AUTHENTICATION_CLASSES': [
# 'rest_framework.authentication.SessionAuthentication',
# 'rest_framework.authentication.BasicAuthentication'
'rest_framework_jwt.authentication.JSONWebTokenAuthentication'
],
}
1.2 局部配置身份认证模块
在视图类中用authentication_classes类属性配置身份认证模块:
# views.py
from rest_framework_jwt.authentication import JSONWebTokenAuthentication
class UserListViewSet(mixins.ListModelMixin, GenericViewSet):
authentication_classes = [JSONWebTokenAuthentication]
queryset = models.User.objects.filter(is_active=True).all()
serializer_class = serializers.UserModelSerializer
2、drf提供的身份认证类(了解)
其中BaseAuthentication
是用来自定义身份认证类需要继承的基类。
其他的类是drf默认提前写好的几种身份认证类,可以直接使用:
BasicAuthentication
SessionAuthentication
TokenAuthentication
def get_authorization_header(request):
# 前台在请求头用authorization携带认证字符串token给后台
auth = request.META.get('HTTP_AUTHORIZATION', b'')
# auth == token字符串 将其编码成二进制
if isinstance(auth, str):
# Work around django test client oddness
auth = auth.encode(HTTP_HEADER_ENCODING)
return auth
class BasicAuthentication(BaseAuthentication):
www_authenticate_realm = 'api'
def authenticate(self, request):
auth = get_authorization_header(request).split()
# auth按空格拆分,拆分的列表结果长度为2才合法
if not auth or auth[0].lower() != b'basic':
# 没有token,认证方法直接返回None,代表游客(匿名用户)
return None
# 可以推论出auth拆分的结果为2份,结构为:['basic','token字符串']
if len(auth) == 1:
msg = _('Invalid basic header. No credentials provided.')
raise exceptions.AuthenticationFailed(msg)
elif len(auth) > 2:
msg = _('Invalid basic header. Credentials string should not contain spaces.')
raise exceptions.AuthenticationFailed(msg)
# 结论:提交了token,格式有误,抛异常,代表非法用户
try:
# 反解token(auth是被拆分的列表,0是头,1是token)
auth_parts = base64.b64decode(auth[1]).decode(HTTP_HEADER_ENCODING).partition(':')
except (TypeError, UnicodeDecodeError, binascii.Error):
msg = _('Invalid basic header. Credentials not correctly base64 encoded.')
raise exceptions.AuthenticationFailed(msg)
# 结论:提交了token,格式有误,抛异常,代表非法用户
userid, password = auth_parts[0], auth_parts[2]
return self.authenticate_credentials(userid, password, request)
# 结论:提交了token,解析成功,返回(user,None)组成的元组,代表合法用户
# 元组0位user会被储存到request.user中,
# 元组1位token会被存储到request.auth中,通常可以不用保存,所以可以用None填充
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
class SessionAuthentication(BaseAuthentication):
def authenticate(self, request):
# Get the session-based user from the underlying HttpRequest object
user = getattr(request._request, 'user', None)
# Unauthenticated, CSRF validation not required
if not user or not user.is_active:
return None
self.enforce_csrf(request)
# CSRF passed with authenticated user
return (user, None)
class TokenAuthentication(BaseAuthentication):
keyword = 'Token'
model = None
def authenticate(self, request):
auth = get_authorization_header(request).split()
if not auth or auth[0].lower() != self.keyword.lower().encode():
return None
if len(auth) == 1:
msg = _('Invalid token header. No credentials provided.')
raise exceptions.AuthenticationFailed(msg)
elif len(auth) > 2:
msg = _('Invalid token header. Token string should not contain spaces.')
raise exceptions.AuthenticationFailed(msg)
try:
token = auth[1].decode()
except UnicodeError:
msg = _('Invalid token header. Token string should not contain invalid characters.')
raise exceptions.AuthenticationFailed(msg)
return self.authenticate_credentials(token)
3、rf-jwt提供的身份认证类(常用)
rf-jwt为我们提供了其已经写好的身份认证类:JSONWebTokenAuthentication
特点:
前端在向后端发送请求时需携带的token格式为:
{'Authorization':'jwt abc.def.xyz'} // token需以jwt开头
后端如何配置:
from rest_framework_jwt.authentication import JSONWebTokenAuthentication
class UserListViewSet(ListModelMixin,GenericViewSet):
# ---------------三大认证配置---------------我是分割线
# authentication_classes = [authentications.MyAuthentication]
authentication_classes = [JSONWebTokenAuthentication]
# -----------------正常逻辑-----------------我是分割线
queryset = models.User.objects.filter(is_active=True).all()
serializer_class = serializers.UserModelSerializer
4、自定义身份认证类(需要自定义签发token时用)
- 如果使用session认证,drf默认提供了SessionAuthentication
- 如果使用drf-jwt认证框架,drf-jwt框架提供了JSONWebTokenAuthentication
- 如果是自定义签发与校验token,才需要将校验token的算法封装到自定义的认证类中
如何自定义身份认证类:
- 继承BaseAuthentication续写Authentication身份认证类:
- 重写authenticate方法;
从请求头中拿到前台提交的token(一般从HTTP_AUTHORIZATION中拿,也可以与前台约定)
- 如果设置了反爬等措施,校验一下反爬(头 token)
没有token,返回None,代表游客
有token,进入校验
- 不通过:抛AuthenticationFailed异常,代表非法用户
- 通过:返回 (user, token),代表合法用户
from rest_framework.authentication import BaseAuthentication
class MyAuthentication(BaseAuthentication):
def authenticate(self, request):
# 不设置任何身份认证规则,直接全部通过,进入下一轮认证(权限认证)
pass
5、自定义签发token及多方式登陆
重点:
- token只能由 登录接口 签发;
- 登录接口也是APIView的子类,使用一定会进行'身份认证'和'权限认证'组件的校验。
结论:不管系统默认、或是全局settings配置的是何认证与权限组件,登录接口不用参与任何认证与权限的校验所以,登录接口一定要进行'身份认证'与'权限认证'的局部禁用
# views.py
from rest_framework.views import APIView
class LoginAPIView(APIView):
authentication_classes = []
pagination_class = []
permission_classes = []
def post(self,request,*args,**kwargs):
user_ser = serializers.LoginModelSerializer(data=request.data)
user_ser.is_valid(raise_exception=True)
return APIResponse(results={
'username':user_ser.content.get('user').username,
'token':user_ser.content.get('token')
})
# serializers.py
from rest_framework_jwt.serializers import jwt_payload_handler,jwt_encode_handler
class LoginModelSerializer(serializers.ModelSerializer):
username = serializers.CharField(
max_length=64,
min_length=3
)
password = serializers.CharField(
max_length=64,
min_length=3
)
class Meta:
model = models.User
fields = ['username','password']
# 在全局钩子中完成token的签发
def validate(self, attrs):
# 先从model表中查出user对象
user = self._validate_user(attrs)
# 将user对象包装进载荷中
payload = jwt_payload_handler(user)
# 将载荷签发入token
token = jwt_encode_handler(payload)
# 将对象和token储存进serializer对象中,就可以在视图类中调用
self.content = {
'user':user,
'token':token
}
return attrs
def _validate_user(self,attrs):
username = attrs.get('username')
password = attrs.get('password')
# 多方式登陆
if re.match(r'.*@.*',username):
user = models.User.objects.filter(email=username).first() # type:models.User
elif re.match(r'^1[3-9][0-9]{9}$',username):
user = models.User.objects.filter(mobile=username).first() # type:models.User
else:
user = models.User.objects.filter(username=username).first() # type:models.User
if not user or not user.check_password(password):
raise serializers.ValidationError({'message':'用户信息系有误!请重新登录!'})
return user
二、权限认证
1、权限认证配置
1.1 全局配置权限认证模块
一般权限认证不做全局配置,因为每个功能对应不同的权限,不好配。
1.2 局部配置权限认证模块
使用permission_classes类属性配置:
from rest_framework.permissions import IsAdminUser,IsAuthenticated,IsAuthenticatedOrReadOnly,AllowAny
class UserViewSet(ViewSet):
# 配置django默认提供的权限认证模块
permission_classes = [IsAuthenticated]
2、drf提供的权限认证类
drf默认提供了几种权限认证模块:
from rest_framework.permissions import IsAuthenticated, IsAdminUser, AllowAny, IsAuthenticatedOrReadOnly
- AllowAny | 游客和登录用户有全权限
- IsAuthenticated | 只有登录用户有全权限
- IsAdminUser | 只有后台用户(admin用户)有全权限
- IsAuthenticatedOrReadOnly | 游客有读权限,登录用户有全权限
3、自定义权限认证类
如果有特殊需要,需要自定义权限类
如:只有superuser有权限、只有vip用户有权限、只有某ip网段用户有权限、只有某个视图及其子类有权限
如何自定义权限类:
- 继承BasePermission续写permission类;
- 重写has_permission方法;
- 根据需求,request和view的辅助,制定权限规则判断条件
- 如果条件通过,返回True
- 如果条件不通过,返回False
# permission.py
from rest_framework.permissions import BasePermission
# VIP用户权限
class VIPUserPermission(BasePermission):
def has_permission(self, request, view):
for group in request.user.groups.all():
# 存在于vip组即有权限
if group.name.lower() == 'vip':
return True
# 否则没有权限
return False
# views.py
class UserViewSet(ViewSet):
# 局部配置哪些权限可以执行此操作
permission_classes = [permissions.VIPUserPermission]
def retrieve(self,request,*args,**kwargs):
return APIResponse(results={
'username':request.user.username,
'email':request.user.email,
'mobile':request.user.mobile,
'create_time':request.user.date_joined,
})
三、节流认证(频率认证)
1、节流认证配置
需全局与局部配置配合。
局部配置节流模式,全局配置节流模式的流量。
1.1 全局配置节流认证模块
# settings.py
REST_FRAMEWORK = {
# 频率组件:频率类一般做局部配置,但是频率调节在settings中配置
'DEFAULT_THROTTLE_RATES': {
'user': '5/min',
'anon': '3/min',
'mobile': '1/min'
},
}
1.2 局部配置节流认证模块
使用throttle_classes类属性配置:
from rest_framework.viewsets import ViewSet
class UserViewSet(ViewSet):
throttle_classes = []
2、drf提供的节流认证类
drf默认提供了几种节流认证模式:
from rest_framework.throttling import AnonRateThrottle,UserRateThrottle,ScopedRateThrottle
- AnonRateThrottle | scope = 'anon'
- UserRateThrottle | scope = 'user'
- ScopedRateThrottle | scope_attr = 'throttle_scope'
3、自定义节流认证类
如果有特殊需要,需要自定义频率类.
如:对ip进行限次、对电话进行限制、对视图某些信息进行限次.
如何自定义:
- 继承SimpleRateThrottle节流基类续写throrrle类;
- 设置scope字符串类属性,同时在settings中进行drf配置DEFAULT_THROTTLE_RATES
- eg: DEFAULT_THROTTLE_RATES = {'mobile': '1/min'}
- 重写get_catch_key方法
- 返回与限制条件有关的字符串,表示限制
- 返回None,表示不限制
# throttles.py
from rest_framework.throttling import SimpleRateThrottle
class ModileRateThrottle(SimpleRateThrottle):
scope = 'mobile'
def get_cache_key(self, request, view):
# 游客和没有手机号的用户不做限制
if not request.user.is_authenticated or request.user.mobile:
return None
# 设置用户唯一识别码
return self.cache_format % {
'scope':self.scope,
'ident':request.user.mobile
}
# views.py
from rest_framework.viewsets import ViewSet
class UserViewSet(ViewSet):
# throttle_classes = [UserRateThrottle]
throttle_classes = [throttles.ModileRateThrottle]
def retrieve(self,request,*args,**kwargs):
return APIResponse(results={
'username':request.user.username,
'email':request.user.email,
'mobile':request.user.mobile,
'create_time':request.user.date_joined,
})
DRF 三大认证的配置及使用方法的更多相关文章
- 8) drf 三大认证 认证 权限 频率
一.三大认证功能分析 1)APIView的 dispath(self, request, *args, **kwargs) 2)dispath方法内 self.initial(request, *ar ...
- drf三大认证解析
目录 三大认证 认证模块: 权限模块 频率模块 RABC author组件 认证权限六表. Content_type 认证与权限工作原理+自定义认证类 自定义权限类 admin关联自定义用户表 前后台 ...
- drf三大认证:认证组件-权限组件-权限六表-自定义认证组件的使用
三大认证工作原理简介 认证.权限.频率 源码分析: from rest_framework.views import APIView 源码分析入口: 内部的三大认证方法封装: 三大组件的原理分析: 权 ...
- drf三大认证
源码分析 """ 1)APIView的dispath(self, request, *args, **kwargs) 2)dispath方法内 self.initial( ...
- DRF 三大认证之身份认证
目录 路由组件补充 三大认证 一.身份认证 1.如何进行身份认证 2.jwt认证规则原理 3.jwt的组成 4.jwt的使用方法 4.1 签发算法 4.2 校验算法 4.3 刷新算法 二.权限认证 三 ...
- drf三大认证补充
频率认证 源码分析部分 def check_throttles(self, request): for throttle in self.get_throttles(): if not throttl ...
- DRF框架(六)——三大认证组件之认证组件、权限组件
drf认证组件 用户信息表 from django.db import models from django.contrib.auth.models import AbstractUser class ...
- drf框架 - 三大认证组件 | 认证组件 | 权限组件 | 频率组件
RBAC 基于用户权限访问控制的认证 - Role-Based Access Control Django框架采用的是RBAC认证规则,RBAC认证规则通常会分为 三表规则.五表规则,Django采用 ...
- drf的三大认证
目录 三大认证任务分析 auth组件的认证权限六表 自定义User表分析 源码分析 认证与权限工作原理 源码分析 认证模块工作原理 权限模块工作原理 admin关联自定义用户表 自定义认证.权限类 用 ...
随机推荐
- Thank in Java
Think in Java 2.一切都是对象 2.1 引用操作对象 Java 中一切都是对象,因此可以采用单一固定得语法. 操作对象得标识符实际上是对对象得一个 "引用"refer ...
- 【实战】通过Python实现疫情地图可视化
目录 一. json模块 二.通过Python实现疫情地图可视化 2.将json格式的数据保存到Excel 3.应用pyecharts进行数据可视化 一. json模块 JSON(JavaScript ...
- Python实现量子态采样
什么是量子态矢量? 在前面一篇量子系统模拟的博客中,我们介绍了使用python去模拟一个量子系统演化的过程.当我们尝试理解量子态和量子门操作时,可以通过其矩阵形式的运算来描述量子态演化的过程: \[\ ...
- DICOM医学文件的解析
最近导师一直让做智慧医疗的一个项目,这里面涉及到DICOM格式的文件处理,在这里分享一下自己学到的关于DCM文件的一些内容. DICOM DICOM(DigitalImaging andCommuni ...
- Java常用类库2
1.java.util.Date类 package LESSON9; import java.util.Date; public class demo1 { public static void ma ...
- CF 1405E Fixed Point Removal【线段树上二分】
CF 1405E Fixed Point Removal[线段树上二分] 题意: 给定长度为\(n\)的序列\(A\),每次操作可以把\(A_i = i\)(即值等于其下标)的数删掉,然后剩下的数组 ...
- 【uva 120】Stacks of Flapjacks(算法效率--构造法+选择排序思想)
题意:有N张正在锅里的一叠煎饼,每张都有一个数字,代表其大小.厨师每次可以选择一个数k,把从锅底开始数第k张上面的煎饼全部翻过来,即原来在上面的煎饼现在到了下面.要求设计一种方法使得所有煎饼按照从小到 ...
- Codeforces 1144F Graph Without Long Directed Paths DFS染色
题意: 输入一张有向图,无自回路和重边,判断能否将它变为有向图,使得图中任意一条路径长度都小于2. 如果可以,按照输入的边的顺序输出构造的每条边的方向,构造的边与输入的方向一致就输出1,否则输出0. ...
- Codeforces Global Round 8 D. AND, OR and square sum (贪心,位运算)
题意:有\(n\)个数,选择某一对数使二者分别\(or\)和\(and\)得到两个新值,求操作后所有数平方和的最大值. 题解:不难发现每次操作后,两个数的二进制表示下的\(1\)的个数总是不变的,所以 ...
- 多线程之ThreadLocal类
深入研究java.lang.ThreadLocal类 0.前言 ThreadLocal(线程变量副本)Synchronized实现内存共享,ThreadLocal为每个线程维护一个本地变量.采用空间换 ...