drf token刷新配置、认证组件(使用)、权限组件(使用)、频率组件(使用)、异常组件(使用)

1、特殊路由映射的请求

实现用户中心信息自查,不带主键的get请求,走单查逻辑算法

urls.py数据库

# 用路由组件配置,造成的映射关系是 /user/center/ => list  |  user/center/(pk)/ => retrieve
# router.register('user/center', views.UserCenterViewSet, 'center')

urlpatterns = [
    # ...
    # /user/center/ => 单查,不能走路由组件,只能自定义配置映射关系
    url('^user/center/$', views.UserCenterViewSet.as_view({'get': 'user_center'})),
]

views.pydjango

from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
class UserCenterViewSet(GenericViewSet):
    permission_classes = [IsAuthenticated, ]
    queryset = models.User.objects.filter(is_active=True).all()
    serializer_class = serializers.UserCenterSerializer

    def user_center(self, request, *args, **kwargs):
        # request.user就是前台带token,在通过认证组件解析出来的,
        # 再通过权限组件IsAuthenticated的校验,因此request.user必定有值,就是当前登陆用户
        serializer = self.get_serializer(request.user)
        return Response(serializer.data)

2、token刷新机制配置(了解)

drf-jwt直接提供刷新功能后端

"""
1)运用在像12306这样极少数安全性要求高的网站
2)第一个token由登陆签发
3)以后的全部正常逻辑,都须要发送两次请求,第一次是刷新token的请求,第二次是正常逻辑的请求
"""

settings.pyapi

import datetime

JWT_AUTH = {
    # 配置过时时间
    'JWT_EXPIRATION_DELTA': datetime.timedelta(minutes=5),

    # 是否可刷新
    'JWT_ALLOW_REFRESH': True,
    # 刷新过时时间
    'JWT_REFRESH_EXPIRATION_DELTA': datetime.timedelta(days=7),
}

urls.py安全

from rest_framework_jwt.views import ObtainJSONWebToken, RefreshJSONWebToken
urlpatterns = [
    url('^login/$', ObtainJSONWebToken.as_view()),  # 登陆签发token接口
    url('^refresh/$', RefreshJSONWebToken.as_view()),  # 刷新toekn接口
]

Postman服务器

# 接口:/api/refresh/
# 方法:post
# 数据:{"token": "登陆签发的token"}

3、认证组件项目使用:多方式登陆

注意:登陆是post请求框架

一、urls.py 路由

# 自定义登陆(重点):post请求 => 查操做(签发token返回给前台) - 自定义路由映射
url('^user/login/$', views.LoginViewSet.as_view({'post': 'login'})),

二、views.py 视图

# 重点:自定义login,完成多方式登陆
from rest_framework.viewsets import ViewSet
from rest_framework.response import Response
class LoginViewSet(ViewSet):
    # 登陆接口,要取消全部的认证与权限规则,也就是要作局部禁用操做(空配置)
    authentication_classes = []
    permission_classes = []

    # 须要和mixins结合使用,继承GenericViewSet,不须要则继承ViewSet
    # 为何继承视图集,不去继承工具视图或视图基类,由于视图集能够自定义路由映射:
    #       能够作到get映射get,get映射list,还能够作到自定义(灵活)
    def login(self, request, *args, **kwargs):
        serializer = serializers.LoginSerializer(data=request.data, context={'request': request})  # 自定义序列化
        serializer.is_valid(raise_exception=True)  # 序列化校验
        token = serializer.context.get('token')  # 获取token
        return Response({"token": token})

三、serializers.py 序列化

from rest_framework_jwt.serializers import jwt_payload_handler, jwt_encode_handler

# 重点:自定义login,完成多方式登陆
class LoginSerializer(serializers.ModelSerializer):
    # 登陆请求,走的是post方法,默认post方法完成的是create入库校验,因此惟一约束的字段,会进行数据库惟一校验,致使逻辑相悖
    # 须要覆盖系统字段,自定义校验规则,就能够避免完成多余的没必要要校验,如惟一字段校验
    username = serializers.CharField()
    class Meta:
        model = models.User
        # 结合前台登陆布局:采用帐号密码登陆,或手机密码登陆,布局一致,因此无论帐号仍是手机号,都用username字段提交的
        fields = ('username', 'password')

    def validate(self, attrs):
        # 在全局钩子中,才能提供提供的所需数据,总体校验获得user
        # 再就能够调用签发token算法(drf-jwt框架提供的),将user信息转换为token
        # 将token存放到context属性中,传给外键视图类使用
        user = self._get_user(attrs)
        payload = jwt_payload_handler(user)  # 内部提供的方法,生成tokrn
        token = jwt_encode_handler(payload)  # 内部提供的方法,生成token
        self.context['token'] = token       # 吧token放进context中,方便取
        return attrs

    # 多方式登陆
    def _get_user(self, attrs):
        username = attrs.get('username')
        password = attrs.get('password')
        import re
        if re.match(r'^1[3-9][0-9]{9}$', username):
            # 手机登陆
            user = models.User.objects.filter(mobile=username, is_active=True).first()
        elif re.match(r'^.+@.+$', username):
            # 邮箱登陆
            user = models.User.objects.filter(email=username, is_active=True).first()
        else:
            # 帐号登陆
            user = models.User.objects.filter(username=username, is_active=True).first()
        if user and user.check_password(password):
            return user

        raise ValidationError({'user': 'user error'})

四、models.py 表

from django.contrib.auth.models import AbstractUser
from django.db import models


class User(AbstractUser):  # settings.py中要配置
    mobile = models.CharField(max_length=11, unique=True, verbose_name='移动电话')
    icon = models.ImageField(upload_to='icon', default='icon/default.png', verbose_name='头像')

    class Meta:
        db_table = 'o_user'
        verbose_name_plural = '用户表'

    def __str__(self):
        return self.username


class Book(models.Model):
    name = models.CharField(max_length=64, verbose_name='书名')

    class Meta:
        db_table = 'o_book'
        verbose_name_plural = '书表'

    def __str__(self):
        return self.name


class Car(models.Model):
    name = models.CharField(max_length=64, verbose_name='车名')

    class Meta:
        db_table = 'o_car'
        verbose_name_plural = '车表'

    def __str__(self):
        return self.name

4、权限组件项目使用:vip用户权限

数据准备

"""
1)User表建立两条数据
2)Group分组表建立一条数据,name叫vip
3)操做User和Group的关系表,让1号用户属于1号vip组
"""

一、permissions.py 自定义权限

from rest_framework.permissions import BasePermission

from django.contrib.auth.models import Group
class IsVipUser(BasePermission):
    def has_permission(self, request, view):
        if request.user and request.user.is_authenticated:  # 必须是合法用户
            try:
                vip_group = Group.objects.get(name='vip')
                if vip_group in request.user.groups.all():  # 用户可能不属于任何分组
                    return True  # 必须是vip分组用户
            except:
                pass

        return False

二、views.py 权限视图

from .permissions import IsVipUser
class CarViewSet(ModelViewSet):
    permission_classes = [IsVipUser] # 自定义校验

    queryset = models.Car.objects.all()
    serializer_class = serializers.CarSerializer

三、serializers.py 序列化

class CarSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Car
        fields = ('name', )

四、urls.py 自定义路由

router.register('cars', views.CarViewSet, 'car')

五、setting.py 权限配置

from rest_framework import settings
# drf配置(把配置放在最下方)
REST_FRAMEWORK = {
    # 自定义三大认证配置类们
    'DEFAULT_AUTHENTICATION_CLASSES':['rest_framework_jwt.authentication.JSONWebTokenAuthentication'], # 认证
    # 'DEFAULT_PERMISSION_CLASSES': [],         # 权限
    # 'DEFAULT_THROTTLE_CLASSES': [],           # 频率
  }

5、频率组件

频率限制源码分析

重点

"""
1)如何自定义频率类
2)频率校验的规则
3)自定义频率类是最多见的:短信接口一分钟只能发生一条短信
"""

系统频率类

#1)UserRateThrottle: 限制全部用户访问频率
#2)AnonRateThrottle:只限制匿名用户访问频率

自定义频率类

from rest_framework.throttling import SimpleRateThrottle
"""
1)自定义类继承SimpleRateThrottle
2)设置类实现scope,值就是一个字符串,与settings中的DEFAULT_THROTTLE_RATES进行对应
    DEFAULT_THROTTLE_RATES就是设置scope绑定的类的频率规则:1/min 就表明一分钟只能访问一次
3)重写 get_cache_key(self, request, view) 方法,指定限制条件
    不知足限制条件,返回None:表明对这类请求不进行频率限制
    知足限制条件,返回一个字符串(是动态的):表明对这类请求进行频率限制
        短信频率限制类,返回 "throttling_%(mobile)s" % {"mobile": 实际请求来的电话}
"""

一、throttles.py 自定义频率,和请求方式限制

from rest_framework.throttling import SimpleRateThrottle
# 只限制查接口的频率,不限制增删改的频率
class MethodRateThrottle(SimpleRateThrottle):
    scope = 'method'    # 系统的配置就放在配置文件中
    def get_cache_key(self, request, view):
        # 只有对get请求进行频率限制
        if request.method.lower() not in ('get', 'head', 'option'):
            return None

        # 区别不一样的访问用户,之间的限制是不冲突的
        if request.user.is_authenticated:
            ident = request.user.pk
        else:
            # get_ident是BaseThrottle提供的方法,会根据请求头,区别匿名用户,
            # 保证不一样客户端的请求都是表明一个独立的匿名用户
            ident = self.get_ident(request)
        return self.cache_format % {'scope': self.scope, 'ident': ident}

二、settings.py 频率配置

REST_FRAMEWORK = {
    #  ...
    # 频率规则配置
    'DEFAULT_THROTTLE_RATES': {
        # 只能设置 s,m,h,d,且只须要第一个字母匹配就ok,m = min = maaa 就表明分钟
        'user': '3/min',  # 配合drf提供的 UserRateThrottle 使用,限制全部用户访问频率
        'anon': '3/min',  # 配合drf提供的 AnonRateThrottle 使用,只限制匿名用户访问频率
        'method': '3/min', # 限制请求方式频率
    },
}

三、views.py 频率视图

from .permissions import IsVipUser
from .throttles import MethodRateThrottle
class CarViewSet(ModelViewSet):
    permission_classes = [IsVipUser]    # 自定义是不是VIP校验
    throttle_classes = [MethodRateThrottle] # 自定义频率限制
    
    queryset = models.Car.objects.all()
    serializer_class = serializers.CarSerializer

6、异常组件项目使用:记录异常信息到日志文件

exception.py 处理后端异常

主要:后端异常以后必定要添加日志记录。配合日志一块儿使用ide

from rest_framework.views import exception_handler as drf_exception_handler
from rest_framework.response import Response
def exception_handler(exc, context):
    # 只处理客户端异常,不处理服务器异常,
    # 若是是客户端异常,response就是能够直接返回给前台的Response对象
    response = drf_exception_handler(exc, context)

    if response is None:
        # 没有处理的服务器异常,处理一下
        # 其实给前台返回 服务器异常 几个字就好了
        # 那咱们处理异常模块的目的是 无论任何错误,都有必要进行日志记录(线上项目只能经过记录的日志查看出现过的错误)
        response = Response({'detail': '%s' % exc})

    # 须要结合日志模块进行日志记录的:项目中讲
    return response

settings.py 异常配置

REST_FRAMEWORK = {
    # ...
    # 异常模块
    # 'EXCEPTION_HANDLER': 'rest_framework.views.exception_handler',  # 原来的,只处理客户端异常
    'EXCEPTION_HANDLER': 'api.exception.exception_handler',
}
相关文章
相关标签/搜索