三大认证

一、身份认证

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时用)

  1. 如果使用session认证,drf默认提供了SessionAuthentication
  2. 如果使用drf-jwt认证框架,drf-jwt框架提供了JSONWebTokenAuthentication
  3. 如果是自定义签发与校验token,才需要将校验token的算法封装到自定义的认证类中

如何自定义身份认证类:

  1. 继承BaseAuthentication续写Authentication身份认证类:
  2. 重写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及多方式登陆

重点:

  1. token只能由 登录接口 签发;
  2. 登录接口也是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网段用户有权限、只有某个视图及其子类有权限

如何自定义权限类:

  1. 继承BasePermission续写permission类;
  2. 重写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进行限次、对电话进行限制、对视图某些信息进行限次.

如何自定义:

  1. 继承SimpleRateThrottle节流基类续写throrrle类;
  2. 设置scope字符串类属性,同时在settings中进行drf配置DEFAULT_THROTTLE_RATES
    • eg: DEFAULT_THROTTLE_RATES = {'mobile': '1/min'}
  3. 重写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 三大认证的配置及使用方法的更多相关文章

  1. 8) drf 三大认证 认证 权限 频率

    一.三大认证功能分析 1)APIView的 dispath(self, request, *args, **kwargs) 2)dispath方法内 self.initial(request, *ar ...

  2. drf三大认证解析

    目录 三大认证 认证模块: 权限模块 频率模块 RABC author组件 认证权限六表. Content_type 认证与权限工作原理+自定义认证类 自定义权限类 admin关联自定义用户表 前后台 ...

  3. drf三大认证:认证组件-权限组件-权限六表-自定义认证组件的使用

    三大认证工作原理简介 认证.权限.频率 源码分析: from rest_framework.views import APIView 源码分析入口: 内部的三大认证方法封装: 三大组件的原理分析: 权 ...

  4. drf三大认证

    源码分析 """ 1)APIView的dispath(self, request, *args, **kwargs) 2)dispath方法内 self.initial( ...

  5. DRF 三大认证之身份认证

    目录 路由组件补充 三大认证 一.身份认证 1.如何进行身份认证 2.jwt认证规则原理 3.jwt的组成 4.jwt的使用方法 4.1 签发算法 4.2 校验算法 4.3 刷新算法 二.权限认证 三 ...

  6. drf三大认证补充

    频率认证 源码分析部分 def check_throttles(self, request): for throttle in self.get_throttles(): if not throttl ...

  7. DRF框架(六)——三大认证组件之认证组件、权限组件

    drf认证组件 用户信息表 from django.db import models from django.contrib.auth.models import AbstractUser class ...

  8. drf框架 - 三大认证组件 | 认证组件 | 权限组件 | 频率组件

    RBAC 基于用户权限访问控制的认证 - Role-Based Access Control Django框架采用的是RBAC认证规则,RBAC认证规则通常会分为 三表规则.五表规则,Django采用 ...

  9. drf的三大认证

    目录 三大认证任务分析 auth组件的认证权限六表 自定义User表分析 源码分析 认证与权限工作原理 源码分析 认证模块工作原理 权限模块工作原理 admin关联自定义用户表 自定义认证.权限类 用 ...

随机推荐

  1. Thank in Java

    Think in Java 2.一切都是对象 2.1 引用操作对象 Java 中一切都是对象,因此可以采用单一固定得语法. 操作对象得标识符实际上是对对象得一个 "引用"refer ...

  2. 【实战】通过Python实现疫情地图可视化

    目录 一. json模块 二.通过Python实现疫情地图可视化 2.将json格式的数据保存到Excel 3.应用pyecharts进行数据可视化 一. json模块 JSON(JavaScript ...

  3. Python实现量子态采样

    什么是量子态矢量? 在前面一篇量子系统模拟的博客中,我们介绍了使用python去模拟一个量子系统演化的过程.当我们尝试理解量子态和量子门操作时,可以通过其矩阵形式的运算来描述量子态演化的过程: \[\ ...

  4. DICOM医学文件的解析

    最近导师一直让做智慧医疗的一个项目,这里面涉及到DICOM格式的文件处理,在这里分享一下自己学到的关于DCM文件的一些内容. DICOM DICOM(DigitalImaging andCommuni ...

  5. Java常用类库2

    1.java.util.Date类 package LESSON9; import java.util.Date; public class demo1 { public static void ma ...

  6. CF 1405E Fixed Point Removal【线段树上二分】

    CF 1405E Fixed Point Removal[线段树上二分]  题意: 给定长度为\(n\)的序列\(A\),每次操作可以把\(A_i = i\)(即值等于其下标)的数删掉,然后剩下的数组 ...

  7. 【uva 120】Stacks of Flapjacks(算法效率--构造法+选择排序思想)

    题意:有N张正在锅里的一叠煎饼,每张都有一个数字,代表其大小.厨师每次可以选择一个数k,把从锅底开始数第k张上面的煎饼全部翻过来,即原来在上面的煎饼现在到了下面.要求设计一种方法使得所有煎饼按照从小到 ...

  8. Codeforces 1144F Graph Without Long Directed Paths DFS染色

    题意: 输入一张有向图,无自回路和重边,判断能否将它变为有向图,使得图中任意一条路径长度都小于2. 如果可以,按照输入的边的顺序输出构造的每条边的方向,构造的边与输入的方向一致就输出1,否则输出0. ...

  9. Codeforces Global Round 8 D. AND, OR and square sum (贪心,位运算)

    题意:有\(n\)个数,选择某一对数使二者分别\(or\)和\(and\)得到两个新值,求操作后所有数平方和的最大值. 题解:不难发现每次操作后,两个数的二进制表示下的\(1\)的个数总是不变的,所以 ...

  10. 多线程之ThreadLocal类

    深入研究java.lang.ThreadLocal类 0.前言 ThreadLocal(线程变量副本)Synchronized实现内存共享,ThreadLocal为每个线程维护一个本地变量.采用空间换 ...