drf——登录功能认证权限频率组件(Django转换器配置文件作用)

Posted XxMa

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了drf——登录功能认证权限频率组件(Django转换器配置文件作用)相关的知识,希望对你有一定的参考价值。

Django转换器、配置文件作用

# django转换器 2.x以后 为了取代re_path
	int	 path(\'books/<int:pk>\')--->/books/1--->pk=1--->当参数传入视图类的方法中
    str  path(\'books/<str:name>\')
    path path(\'media/<path:path>\',serve,\'document_root\':\'路径\' ),
    slug
    uuid 
    
# django配置文件
1.Django项目要运行 优先执行配置文件的内容 做一下配置加载工作 manage.py中
    os.environ.setdefault(\'DJANGO_SETTINGS_MODULE\', \'drf_day07.settings\')
    ##Django项目配置文件的路径可以修改 配置文件名也可以修改 但一般不会去改
    os.environ.setdefault(\'DJANGO_SETTINGS_MODULE\', \'配置文件路径\')
        
2.任何一个Django项目都有两套配置
    一套是项目自己的 自己有哪个配置参数 优先用自己的
    一套是内置的(Django内置的)
        
3.配置参数的作用
    # 1  项目的根路径  
        BASE_DIR
    # 2 密钥---》djagno中涉及到加密的,大部分都会用这个密钥
        SECRET_KEY 
    # 3 是否开启调试模式 上线一定关闭 只要是调试模式,访问路径不存在时,会显示出所有能访问的路径,视图类出了异常,浏览器中能看到
        DEBUG = True 默认是True
    # 4 项目是要部署在某个服务器上 这个列表写部署服务器的ip地址 *表示任何地址都可以
    	ALLOWED_HOSTS = [\'*\'] 默认是空的
    # 5 内置 我们自己写的app
        INSTALLED_APPS = [
            \'django.contrib.admin\',  # 后台管理---》很多表不是它的,是别的app的
            \'django.contrib.auth\',  # auth 模块,UsrInfo表----》有6个表
            \'django.contrib.contenttypes\',  # 有个django_content_type表是,这个app的
            \'django.contrib.sessions\',  # session相关的
            \'django.contrib.messages\',  # 消息框架
            \'django.contrib.staticfiles\',  # 静态文件开启的app
            \'app01.apps.App01Config\',  # app01 自己写的app要在这里注册
            \'rest_framework\'  # drf 浏览器访问需要在这里注册
        ]	
    # 6 中间件
   		MIDDLEWARE = [
    	\'django.middleware.security.SecurityMiddleware\',
    	\'django.contrib.sessions.middleware.SessionMiddleware\',  # session相关
    	\'django.middleware.common.CommonMiddleware\',  # 公共
    	\'django.middleware.csrf.CsrfViewMiddleware\',
    	\'django.contrib.auth.middleware.AuthenticationMiddleware\',
    	\'django.contrib.messages.middleware.MessageMiddleware\',
    	\'django.middleware.clickjacking.XFrameOptionsMiddleware\',
	]   
    # 7 根路由
    	ROOT_URLCONF = \'drf_day07.urls\'  # 默认是urls 也可以改文件名 一般不改
    # 8 模板文件所在路径
    	TEMPLATES = [
            ...
        ]
    # 9 项目上线,运行application,后面再说
    	WSGI_APPLICATION = \'drf_day07.wsgi.application\'
    # 10 数据库配置
    	DATABASES = 
            ...
        
    # 11 做国际化
    	LANGUAGE_CODE = \'zh-hans\'  # 语言
        TIME_ZONE = \'Asia/Shanghai\' # 时区
        USE_I18N = True
        USE_L10N = True
        USE_TZ = True
    # 12 静态文件
		STATIC_URL = \'/static/\'
    # 13 表中,默认可以不写id,id主键自增,之前全是AutoField,长度很短
    	DEFAULT_AUTO_FIELD = \'django.db.models.BigAutoField\'

登录功能

表模型

class UserInfo(models.Model):
    username = models.CharField(max_length=32)
    password = models.CharField(max_length=64)

class UserToken(models.Model):
    token = models.CharField(max_length=64)
    user = models.OneToOneField(to=UserInfo,on_delete=models.CASCADE)

视图类

class UserView(ViewSet):
    @action(methods=[\'POST\'],detail=False)
    def login(self,request):
        username = request.data.get(\'username\')
        password = request.data.get(\'password\')
        user_obj = UserInfo.objects.filter(username=username,password=password).first()
        if user_obj:
            # 登录成功
            # 1 生成一个随机字符串 token
            token = str(uuid.uuid4())
            # 2 把token存到表中 UserToken表有值就更新 没有值就增加
            UserToken.objects.update_or_create(user=user_obj,defaults=\'token\':token)
            return Response(\'code\': 100, \'msg\': \'登录成功\', \'token\': token)
        else:
            return Response(\'code\': 101, \'msg\': \'用户名或密码错误\')

路由

# 方式一:
path(\'login/\',views.UserView.as_view(\'post\':\'login\')),
# 路由如果这样写 是不需要使用action装饰器

# 方式二:自动生成路由---》视图类中一定要用action装饰器
from rest_framework.routers import SimpleRouter

router = SimpleRouter()
router.register(\'user\',views.UserView,\'user\')

urlpatterns = [
    path(\'admin/\', admin.site.urls),
    # path(\'\',include(router.urls))
]
# http://127.0.0.1:8000/user/login/
urlpatterns += router.urls

认证组件

# APIView执行流程
	在视图类的方法之前 执行了三大认证

# 认证:登录认证
	登录认证---》控制某个接口必须登录后才能访问
    
# 认证组件使用步骤(固定用法)
	1 写一个类 继承BaseAuthentication
    	from rest_framework.authentication import BaseAuthentication
    2 在类中重写authenticate方法
    	class LoginAuth(BaseAuthentication):
    		def authenticate(self, request):  # 父类中有 一定要重写 否则报错
        	# 校验用户是否登录---》请求中携带我给的token 就是登录了
        	# token在哪携带 是接口规定的
        	# 1.规定待在请求地址中---》讲这个
        	# 2.规定带在请求头中(这个多)
        	# 3.规定待在请求体中
        	# 取出token META HTTP_TOKEN
        	\'\'\'方法一 从请求地址中取\'\'\'
        	# token = request.query_params.get(\'token\')
        	\'\'\'方法二 从请求头中取\'\'\'
        	token = request.META.get(\'HTTP_TOKEN\')
        	# 去数据库中 根据token 校验有没有数据
        	user_token = UserToken.objects.filter(token=token).first()
        	if user_token:
            	# 如果有就是已经登录
            	# print(request.user)  认证没有结束之前不能打印request.user 否则会递归
            	user = user_token.user
            	print(user_token.user)  # UserInfo object (1)
            	return user,token
        	else:
            	# 说明它带的token不对的
            	raise AuthenticationFailed(\'你没有登录,不能访问\')
    3 在方法中 完成登录认证 如果没有登录则抛异常
    4 如果是登录的 返回登录用户和token
    
    5 在视图类中 使用认证类(局部使用)
		class BookView(APIView):
            authentication_classes = [LoginAuth,]
    6 全局使用:
    ### 重点:不要在配置文件中,导入莫名其妙的包
    REST_FRAMEWORK = 
        \'DEFAULT_AUTHENTICATION_CLASSES\': [
            \'app01.auth.LoginAuth\'
        ],
    
    7 全局使用认证后 局部禁用
    class UserView(ViewSet):
        authentication_classes = []  # 列表置为空就可以
    8 认证类的使用顺序
    	优先用视图类配置的
        其次用项目配置文件
        最后用drf默认的配置
        
# 小重点:一旦通过认证 在request中就有当前登录用户 request.user为当前登录用户
	def get(self,request):
        print(request.user.username,\'访问了接口\')

权限组件

# 大家都登录了,但有的功能(接口),只有超级管理员能做,有的功能所有登录用户都能做----》这就涉及到权限的设计了

# 权限设计:比较复杂---》有acl,rbac,abac。。。

# 咱们现在只是为了先讲明白,drf的权限组件如何用,咱们先以最简单的为例
	查询所有图书:所有登录用户都能访问(普通用户和超级管理员)
        没有权限控制
    删除图书 只有超级管理员能访问
    	给其他用户设置的权限
        
# 权限类的使用步骤
	1.写一个类 继承BasePermission
    	from rest_framework.permissions import BasePermission
    2.在类中写方法:has_permission
      	如果有权限 就返回True
        如果没有权限 就返回False
        错误信息是self.message=\'字符串\'
        class AdminPermission(BasePermission):
    		def has_permission(self, request, view):
        	# 如果有权限 就是返回True 没有权限 返回False
        	# 判断user_type是不是1 根据当前登录用户
        	# request.user # 就是当前登录用户 一旦来到这里,登录认证就通过了
        	if request.user.user_type == 1:
            	return True
        	else:
            	# 错误信息
            	self.message = \'你好:%s,你没有权限\' % request.user.username
            	return False
        
    3.局部使用
    class BookDetailView(APIView):
        permission_classer = [AdminPermission,]
    4.全局使用
        REST_FRAMEWORK = 
        \'DEFAULT_PERMISSION_CLASSES\': [
            \'app01.permission.AdminPermission\'
        ],
        
    5.局部禁用
    class BookView(APIView):
        permission_classer = []

频率组件

# 限制访问频次
	比如某个接口 一分钟只能访问5次 超过了就得等
    按ip地址 限制
    按用户id 限制

# 频率类的使用步骤
	1.写个类 继承:SimpleRateThrottle
    2.重写某个方法:get_cache_key
    	可以返回ip或者用户id
        返回什么就以什么做频率限制
        
    3.写一个类属性 随意命名一个名字
    	scope = \'lqz\'
    4.在配置文件中配置
    	\'DEFAULT_THROTTLE_RATES\': 
        	\'lqz\': \'3/m\' # 一分钟访问3次
    	,
    5.全局用
    	\'DEFAULT_THROTTLE_CLASSES\': [
            
        ],
    6.局部用
    class BookView(APIView):
        throttle_classes = [MyThrottle]

认证权限频率自定义签发token-多方式登录

三大认证流程图

技术图片

路由配置

在应用下新建文件router.py

# router.py
from rest_framework.routers import Route, DynamicRoute, SimpleRouter as DRFSimpleRouter


class SimpleRouter(DRFSimpleRouter):
    routes = [
        # List route.
        Route(
            url=r'^{prefix}{trailing_slash}$',
            mapping={
                'get': 'list',
                'post': 'create',
                'put': 'multiply_update',
                'patch': 'multiply_partial_update',
                'delete': 'multiply_destroy'
            },
            name='{basename}-list',
            detail=False,
            initkwargs={'suffix': 'List'}
        ),
        # Dynamically generated list routes. Generated using
        # @action(detail=False) decorator on methods of the viewset.
        DynamicRoute(
            url=r'^{prefix}/{url_path}{trailing_slash}$',
            name='{basename}-{url_name}',
            detail=False,
            initkwargs={}
        ),
        # Detail route.
        Route(
            url=r'^{prefix}/{lookup}{trailing_slash}$',
            mapping={
                'get': 'retrieve',
                'put': 'update',
                'patch': 'partial_update',
                'delete': 'destroy'
            },
            name='{basename}-detail',
            detail=True,
            initkwargs={'suffix': 'Instance'}
        ),
        # Dynamically generated detail routes. Generated using
        # @action(detail=True) decorator on methods of the viewset.
        DynamicRoute(
            url=r'^{prefix}/{lookup}/{url_path}{trailing_slash}$',
            name='{basename}-{url_name}',
            detail=True,
            initkwargs={}
        ),
    ]


router = SimpleRouter()

urls.py

# urls.py

from django.conf.urls import url, include
from .router import router
from . import views
router.register('users', views.UserListAPIViewSet, basename='user')

urlpatterns = [
    url(r'', include(router.urls))
]

认证组件

配置drf-jwt框架的认证类 认证组件只能决定request.user,不是断定权限的地方,所以一般配置全局

# settings.py

REST_FRAMEWORK = {
    # 认证组件
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework_jwt.authentication.JSONWebTokenAuthentication'
    ],

}

新建文件authentications.py,自定义认证

from rest_framework.authentication import BaseAuthentication

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

from rest_framework_jwt.authentication import JSONWebTokenAuthentication
class MyAuthentication(BaseAuthentication):
    def authenticate(self, request):
        pass
        # 1、从请求头中拿到前台提交的token(一般从HTTP_AUTHORIZATION中拿,也可以与前台约定)
        #       如果设置了反爬
        # 2、没有token,返回None,代表游客
        # 3、有token,进入校验    不通过,抛异常,代表非法用户;通过,返回(user, token),代表合法用户

自定义认证规则:

  • 从请求头中拿到前台提交的token(一般从HTTP_AUTHORIZATION中拿,也可以与前台约定)

    • 如果设置了反爬等措施,校验一下反爬(头 token)
  • 没有token,返回None,代表游客
  • 有token,进入校验
    • 不通过,抛异常,代表非法用户
    • 通过,返回(user, token),代表合法用户

认证组件源码分析:

技术图片

技术图片

权限组件

权限类就是实现 BasePermission类,重写has_permission 方法,如果有权限返回True,没权限返回False。

局部配置权限:

# views.py

# 权限组件
permission_classes = [permissions.MyPermission]

自定义权限类

# permissions.py
from rest_framework.permissions import BasePermission

class MyPermission(BasePermission):
    def has_permission(self, request, view):
        """
        根据需求,request和view的辅助,制定权限规则判断条件
        如果条件通过,返回True
        如果条件不通过,返回False
        """
        pass

配置drf自带的权限类

  • drf默认提供了一些权限类
    • AllowAny:游客和登录用户全权限
    • IsAuthenticated:只有登录用户有全权限
    • IsAdminUser:只有后台用户(admin)有全权限
    • IsAuthenticatedOrReadOnly:游客有读权限,登录用户有全权限
  • 如果有特殊需要,需要自定义权限类
    • 如:只有superuser有权限、只有vip用户有权限、只有某ip网段用户有权限、只有某个视图及其子类有权限
# views.py
from rest_framework.viewsets import GenericViewSet
from rest_framework import mixins
from .response import APIResponse
from rest_framework.permissions import IsAuthenticated

class UserListAPIViewSet(mixins.ListModelMixin, GenericViewSet):
    permission_classes = [IsAuthenticated]
    pass

drf-jwt 签发token源码分析

"""
drf-jwt 签发token
1、username、password 通过auth组件的authenticate方法得到user对象
2、user对象、通过drf-jwt框架的jwt_payload_handler函数包装 payload载荷
3、payload载荷 通过drf-jwt框架的jwt_encode_handler函数签发 token字符串

注:可以借助jwt_payload_handler和jwt_encode_handler 两个函数完成自定义token的签发
"""

多方式登录 签发token

  • token只能 由在登录接口 签发

  • 登录接口也是APIView的子类,使用一定会进行 认证、权限、组件的校验

  • 结论:不管系统默认、或是全局settings配置的是何 认证与权限组件,登录接口不用参与任何认证与权限的校验。所以,登录接口一定要进行 认证与权限的局部禁用

from rest_framework.views import APIView


class LoginAPIView(APIView):
    authentication_classes = []
    permission_classes = []

    def post(self, request, *args, **kwargs):
        serializers = serializer.LoginModelSerializer(data=request.data)
        serializers.is_valid(raise_exception=True)  # 内部在全局钩子中完成token的签发
        return APIResponse(results={
            'username': '',
            'token': ''
        })

这样会有个问题:post方法默认走的是数据库的增方法

默认的校验规则里面是走的数据库,我们可以自定义字段

# 序列化类 serializer.py
from rest_framework_jwt.serializers import jwt_payload_handler, jwt_encode_handler
import re
class LoginModelSerializer(serializers.ModelSerializer):
    username = serializers.CharField(min_length=3, max_length=16)
    password = serializers.CharField(min_length=3, max_length=16)
    class Meta:
        model = models.User
        fields = ['username', 'password']

    # 用全局钩子,完成token的签发
    def validate(self, attrs):
        # 1、通过username、password完成多方式登录校验,得到user对象
        user = self._validate_user(attrs)
        # 2、user对象包装怕payload载荷
        payload = jwt_payload_handler(user)
        # 3、payload载荷签发token
        token = jwt_encode_handler(payload)
        # 4、将user与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()
        elif re.match(r'^1[1-9][0-9]{9}$', username):  # 电话
            user = models.User.objects.filter(mobile=username).first()
        else:  # 用户名
            user = models.User.objects.filter(username=username).first()

        if not user or not user.check_password(password):
            raise serializers.ValidationError({'message': '用户信息异常'})

        return user
# views.py

from rest_framework.views import APIView


class LoginAPIView(APIView):
    authentication_classes = []
    permission_classes = []

    def post(self, request, *args, **kwargs):
        serializers = serializer.LoginModelSerializer(data=request.data)
        serializers.is_valid(raise_exception=True)  # 内部在全局钩子中完成token的签发
        print(serializers.content)
        return APIResponse(results={
            'username': serializers.content.get('user').username,
            'token': serializers.content.get('token')
        })

总结:认证与权限绑定使用

  • 每一个视图类都要进行认证,且认证规则一致,多余全局配置认证即可
  • 每一个视图类都要进行权限校验,默认配置的是 不限制(AllowAny),但实际开发中,视图类的访问权限不完全相同,所以要在具体的视图类,配置具体的权限规则

VIP用户认证权限例子

# views.py

from rest_framework.viewsets import ViewSet
class UserViewSet(ViewSet):
    # 权限:只要VIP用户可以查看 个人详细信息
    permission_classes = [permissions.VIPUserPermission]
    def retrieve(self, request, *args, **kwargs):
        return APIResponse(results={
            'username': request.user.username,
            'email': request.user.username,
            'mobile': request.user.username,
            'data_joined': request.user.date_joined,
        })
# 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():
            if group.name.lower() == 'vip':
                return True
        return False

频率组件

技术图片

技术图片

# views.py
class UserListAPIViewSet(mixins.ListModelMixin, GenericViewSet):
    from rest_framework.throttling import AnonRateThrottle, UserRateThrottle
    # 频率组件
    # throttle_classes = [AnonRateThrottle]
    throttle_classes = [UserRateThrottle]
    
    queryset = models.User.objects.filter(is_active=True).all()
    serializer_class = serializer.UserModelSerializer

    def update(self, request, *args, **kwargs):
        return APIResponse()

    def multiply_update(self, request, *args, **kwargs):
        return APIResponse(msg='群改')

在settings.py配置文件中配置

REST_FRAMEWORK = {
    # 频率组件,频率类一般做局部配置,但是频率调节在settings中配置
    'DEFAULT_THROTTLE_RATES': {
        'user': '5/min',   # 登录用户限制
        'anon': '3/min',   # 匿名用户限制
    },

}

自定义频率类

自定义频率类:

  • drf默认提供了一些频率类
    • AnonRateThrottle:只对游客进行频率限制
    • UserRateThrottle:对所有用户进行频率限制
  • 如果由特殊需要,需要自定义频率类
    • 如:对ip进行限次、对电话进行限次、对视图某些信息进行限次

方法步骤:

  • 设置scope字符串类属性,同时在settings中进行drf配置DEFAULT_THROTTLE_RATES
  • 重写get_catch_key方法
    • 返回与限制条件有关的字符串,表示限制
    • 返回None,表示不限制
# throttles.py

from rest_framework.throttling import SimpleRateThrottle

class MobileReateThrottle(SimpleRateThrottle):

    scope = 'mobile'
    def get_cache_key(self, request, view):
        if not request.user.is_authenticated or not request.user.mobile:
            return None   # 匿名用户   没有电话号的用户都不限制

        return self.cache_format % {
            'scope': self.scope,
            'ident': request.user.mobile
        }

以上是关于drf——登录功能认证权限频率组件(Django转换器配置文件作用)的主要内容,如果未能解决你的问题,请参考以下文章

认证权限频率自定义签发token-多方式登录

drf框架 8 系统权限类使用 用户中心信息自查 token刷新机制 认证组件项目使用:多方式登录 权限组件项目使用:vip用户权限 频率组件 异常组件项目使用

自定义路由组件,Django的admin后台管理,DRF的三大认证,jwt认证

管理表页面的创建

drf 三大认证详解

DRF三大认证