DRF基于角色权限控制 django缓存

DRF

1.MD5和base64区别

base64 编码和解码
nd5长度固定   不可反解

base64 变长 可以反解

#base64编码
import base64
import json

dic={'name':'zzz','age':19}
dic_str=json.dumps(dic)

#使用base64编码必须是bytes类型 二进制
print(dic_str.encode('utf-8'))
ret=base64.b64encode(dic_str.encode('utf-8'))
print(ret)

#base64解码
tmp=base64.b64decode(ret)
print(tmp)

2、手写注册查询

#views视图类
from django.shortcuts import render

# Create your views here.
from rest_framework.viewsets import GenericViewSet
from rest_framework.mixins import CreateModelMixin,RetrieveModelMixin,UpdateModelMixin
from apu import models
from apu import ser
#密码需要加密存入数据库,sava方法
class RegisterView(GenericViewSet,CreateModelMixin,RetrieveModelMixin,UpdateModelMixin):
   queryset = models.User.objects.all()
   serializer_class =ser.UserSerializer

   # 如果get请求和post请求用的序列化类不一样,如何处理
   # 需要重写get_serializer_class 这个类返回什么用的序列化类就是什么
   # 注册用的是RegisterView,查询用的是ListRetriveView
   def get_serializer_class(self):
       print(self.action)  #只可能是create 或者retrieve
       if self.action=='create':
           return ser.UserSerializer
       elif self.action=='retrieve':
           return ser.ListSerializer
       elif self.action=='update':
           #上传文件要用formdata格式的
           return ser.UserImageSerializer
#自定义序列化类
from rest_framework import serializers
from apu import models
from rest_framework.exceptions import ValidationError


class UserSerializer(serializers.ModelSerializer):
   # 第二次输入密码 required:必填   write_only:只能写入数据 不会显示
   re_password = serializers.CharField(max_length=16, min_length=4, required=True, write_only=True)

   class Meta:
       model = models.User
       # icon 非必填 因为设置表时候指定了默认值 ,还可以加required=True,null=True 也可以不用填  其他必须填
       fields = ['username', 'password', 're_password', 'mobile', 'icon']
       extra_kwargs = {
           'username': {'max_length': 16},
           'password': {'write_only': True},

       }

   # 局部钩子 校验手机号
   def validate_mobile(self, mobile):
       if not len(mobile) == 11:
           raise ValidationError('手机号必须是11位')
       return mobile

   # 全局钩子校验两次密码输入是否一致
   def validate(self, attrs):
       if not attrs.get('password') == attrs.get('re_password'):
           raise ValidationError('两次密码不一致')
       # 写入数据库不需要第二次输入的密码,因为数据库没有这个字段
       attrs.pop('re_password')
       return attrs

   # 重写create
   def create(self, validated_data):
       user = models.User.objects.create_user(**validated_data)
       return user


class ListSerializer(serializers.ModelSerializer):
   class Meta:
       model = models.User
       fields = ['username', 'password', 'mobile', 'icon']


class UserImageSerializer(serializers.ModelSerializer):
   class Meta:
       model = models.User
       fields = ['icon']
       
#urls总路由
from django.contrib import admin
from django.urls import path,include,re_path
from django.views.static import serve #django 内置的一个视图函数
from django.conf import settings #获取配置文件用这个 获取的多一点
urlpatterns = [
   path('admin/', admin.site.urls),
   path('api/', include('apu.urls')),
   #开发media文件夹
   re_path('media/(?P<path>.*)', serve,{'document_root':settings.MEDIA_ROOT}),


]
#子路由

from django.contrib import admin
from django.urls import path,include
from rest_framework.routers import DefaultRouter,SimpleRouter
from apu import views
router=SimpleRouter()
router.register('register',views.RegisterView,'register')
# router.register('list',views.ListRetriveView,'list')
urlpatterns = [
   # path('admin/', admin.site.urls),
   # path('api/', include('apu.urls')),

]
urlpatterns += router.urls

#settings 修改的配置
# image指定这两个需要
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')  # 真正文件路径

AUTH_USER_MODEL = 'apu.user'  #app名字.表名小写

3.jwt

1.#可以通过认证类:JSONWebTokenAuthentication 和权限类IsAuthenticated 控制用户必须登录才可以访问的接口
class OrderAPIView(APIView):
    #jwt认证
    authentication_classes = [JSONWebTokenAuthentication,]
    #权限控制
    permission_classes = [IsAuthenticated,]

    def get(self,request,*args,**kwargs):
        return Response('这是订单信息')

#如果用户不登录就可以访问的接口只需要设置JSONWebTokenAuthentication 把权限类IsAuthenticated去掉就ok了
class UserInfoAPIView(APIView):
    #jwt认证
    authentication_classes = [JSONWebTokenAuthentication,]

    def get(self,request,*args,**kwargs):
        return Response('UserInfoAPIView')
2.控制登录接口返回的数据格式
	2.1第一种方案 自己写登录接口
    2.2 第二种方案 用内置,控制返回数据格式
    	jwt的配置信息中
        #响应数据的配置格式
    	'JWT_RESPONSE_PAYLOAD_HANDLER':
    	'rest_framework_jwt.utils.jwt_response_payload_handler',
        
        #可以重写jwt_response_payload_handler来控制返回数据的格式
        

3.1重写jwt_response_payload_handler

#返回什么前端就能看到什么
def My_jwt_response_payload_handler(token, user=None, request=None):

    return {
        'token': token,
        'msg':'登陆成功',
        'status':100,
        'username':user.username
    }

#配置文件
#jwt配置
JWT_AUTH={
    'JWT_RESPONSE_PAYLOAD_HANDLER':'app02.utils.My_jwt_response_payload_handler'

}

3.2测试结果

DRF基于角色权限控制 django缓存

3.3自定义jwt的权限类

#下面这俩导入的都是restframework的BaseAuthentication,因为在jwt里面也导入了BaseAuthertication
from rest_framework.authentication import BaseAuthentication
# from rest_framework_jwt.authentication import BaseJSONWebTokenAuthentication
from rest_framework.exceptions import AuthenticationFailed
# from rest_framework_jwt.authentication import jwt_decode_handler
from rest_framework_jwt.utils import jwt_decode_handler #和上面一毛一样
#异常
import jwt
from apu import models
class MyJwtAuthentication(BaseAuthentication):
    def authenticate(self, request):
        jwt_value=request.META.get('HTTP_AUTHORIZATION')
        if jwt_value:
            #jwt 提供了通过三段token取出payload的方法,并且有校验功能
            try:
                payload=jwt_decode_handler(jwt_value)
            except jwt.ExpiredSignature:
                raise AuthenticationFailed('签名过期')
            except jwt.InvalidTokenError:
                raise AuthenticationFailed('非法用户')
            except Exception as e:
                raise AuthenticationFailed(str(e))
            #因为payload他就是用户信息字典
            print(payload)
            # return payload, jwt_value
            #需要得到user对象 第一种去数据库查
            # user=models.User.objects.get(pk=payload.get('user_id'))
            #第二种不查库
            user=models.User(id=payload.get('user_id'),username=payload.get('username'))
            return user,jwt_value
        #没有值抛异常
        raise AuthenticationFailed('你没有携带认证信息')
        
 ----------------------------       
from rest_framework_jwt.authentication import BaseJSONWebTokenAuthentication
class MyJwtAuthentication(BaseJSONWebTokenAuthentication):
    def authenticate(self, request):
        jwt_value=request.META.get('HTTP_AUTHORIZATION')
        if jwt_value:
            #jwt 提供了通过三段token取出payload的方法,并且有校验功能
            try:
                payload=jwt_decode_handler(jwt_value)
            except jwt.ExpiredSignature:
                raise AuthenticationFailed('签名过期')
            except jwt.InvalidTokenError:
                raise AuthenticationFailed('非法用户')
            except Exception as e:
                raise AuthenticationFailed(str(e))
            #因为payload他就是用户信息字典
            print(payload)
            #这个方法实际上内部也查询了数据库
            user=self.authenticate_credentials(payload)
            return user,jwt_value
        #没有值抛异常
        raise AuthenticationFailed('你没有携带认证信息')

3.4手动签发token

#为了用户的多登录方式
#views
from app02 import ser
#手动签发token 实现多方式登录
from rest_framework.viewsets import ViewSetMixin,ViewSet
class Login2View(ViewSet):
    def login(self,request,*args,**kwargs):
        print('hhhh')
        #1.有一个序列化的类

        #2.生成序列化对象
        login_ser = ser.LoginModelSerializers(data=request.data,context={'request':request})

        #3.调用序列化对象的is_validad
        login_ser.is_valid(raise_exception=True)
        token=login_ser.context.get('token')

        #4.return
        return Response({'status':100,'msg':'登录成功','token':token,'username':login_ser.context.get('username')})
    
    
#自定义序列化
from rest_framework import serializers
from apu import models
import re
from rest_framework.exceptions import ValidationError

# 签发token用到的
from rest_framework_jwt.utils import jwt_encode_handler, jwt_payload_handler


class LoginModelSerializers(serializers.ModelSerializer):
    username = serializers.CharField()

    class Meta:
        model = models.User
        fields = ['username', 'password']

    def validate(self, attrs):
        print(1)
        print(self.context)
        # 在这写逻辑
        username = attrs.get('username')
        password = attrs.get('password')
        # 通过判断,username数据不同,查询的字段不一样
        # 正则匹配,如果是手机号
        if re.match('^1[3-9][0-9]{9}$', username):
            print(2)
            user = models.User.objects.filter(mobile=username).first()
        elif re.match('^.+@.+$', username):
            user = models.User.objects.filter(email=username).first()
        else:
            user = models.User.objects.filter(username=username).first()
        if user:  # 用户存在
            # 校验密码,因为是密文 要用check_password
            if user.check_password(password):
                # 密码正确要签发token
                payload = jwt_payload_handler(user)  # user传入得到payload
                token = jwt_encode_handler(payload)  # payload传入得到token
                self.context['token'] = token
                self.context['username'] = username
                return attrs
            else:
                raise ValidationError('密码错误')
        else:
            raise ValidationError('用户不存在')
#urls
    path('login2/', views.Login2View.as_view({'post':'login'})),

3.5配置过期时间

import datetime
JWT_AUTH={
    'JWT_RESPONSE_PAYLOAD_HANDLER':'app02.utils.My_jwt_response_payload_handler',
    'JWT_EXPIRATION_DELTA': datetime.timedelta(days=7),
}

4.基于角色的权限控制(django内置auth体系)

#RBAC 基于角色的访问控制,公司内部系统
#django 中
	后台的权限控制(公司内部)
	user表
    permssion表
    group表和
    其中user_group表是user表和group表的中间表
    group_permssion是group表和permssion表的中间表
    user_permission是user表和permssion表的中间表
	#前台主站 需要三大认证 认证 、权限、频率

5.django缓存

#前后端混合开发缓存的使用
	缓存的位置,通过文件来操作(以文件为例)
	缓存的粒度
    settings.py文件配置
    CACHES = {
     'default': {
      'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache', #指定缓存使用的引擎
      'LOCATION': '/var/tmp/django_cache',        #指定缓存的路径
      'TIMEOUT':300,              #缓存超时时间(默认为300秒,None表示永不过期)
      'OPTIONS':{
       'MAX_ENTRIES': 300,            # 最大缓存记录的数量(默认300)
       'CULL_FREQUENCY': 3,           # 缓存到达最大个数之后,剔除缓存个数的比例,即:1/CULL_FREQUENCY(默认3)
      }
     }   
    }
    	全站缓存
        	中间件
            	MIDDLEWARE_CLASSES = (
                ‘django.middleware.cache.UpdateCacheMiddleware’, #第一
                ......
                ‘django.middleware.cache.FetchFromCacheMiddleware’, #最后
            )
        -页面缓存
        	在试图上添加装饰器
                from django.views.decorators.cache import cache_page

                @cache_page(5) #缓存五秒钟

                def test_cache(request):
                    pass
        -面局部缓存
            {% load cache %}
            <!DOCTYPE html>
            <html lang="en">
            <head>
                <meta charset="UTF-8">
                <title>Title</title>
            </head>
            <body>
            {% cache 2 'name' %}  #表示缓存2秒钟, name是唯一key值
             <h3>缓存:-----:{{ t }}</h3>
            {% endcache %}

            </body>
            </html>

#前后的分离缓存的使用
	from django.core.cache import cache
    def test(request):
        #set可以放对象,字符串
        cache.set('name','zhang') #通过set、get获取缓存 前面是key 后面是value value可以是任意数据类型
    # 应用场景
    	第一次查询所有图书。通过多表联合查询序列化的数据,直接缓存起来
        然后以后再去缓存查询,如果有直接返回 没有再去数据库查询 然后在缓存
上一篇:django 序列化类树形递归的实现


下一篇:Django 4.0 入门系列