django-restframework-jwt多方式登录、自定义user表及签发token、book表单增删查改等相关内容-90

1 jwt:重点(跟语言,框架无关)
-json web token
   -cookie:客户端浏览器上的键值对,数据不安全
   -session:服务端的键值对(内存,数据库,redis,文件),安全,对服务端压力大
   -token:三段:头.荷载.签名
  -header(公司信息,加密方式。。。)
       -payload(荷载,真正有用的数据部分,用户id,用户名字。。)
       -signature(签名,把头和荷载部分通过加密算法加密---》得到一个签名)
2 drf-jwt模块
-快速使用:(默认使用的是auth的user表)
  -1 创建用户
       -2 在路由中配置path('login/', obtain_jwt_token),
  -3 在postman中测试,用户名密码输入刚刚创建的用户就可以生成token
       -4 让一个视图必须登录以后才能访问
      -authentication_classes = [JSONWebTokenAuthentication, ]
-permission_classes = [IsAuthenticated,]
  -5 让一个视图可以登录后访问,也可以不登录访问
      -authentication_classes = [JSONWebTokenAuthentication, ]
       -6 用postman测试,在请求头中加入
      -Authorization  jwt adfasdf
           
   -自己写基于jwt的认证类(登录了能访问,不登录就不能访问)
  class JwtAuthentication(BaseJSONWebTokenAuthentication):
           def authenticate(self, request):
               #token=request.GET.get('token')
               token=request.META.get('HTTP_Authorization'.upper())
               try:
                   # 验证token是否正确
                   payload = jwt_decode_handler(token)
               except jwt.ExpiredSignature:
                   raise AuthenticationFailed('过期了')
               except jwt.DecodeError:
                   raise AuthenticationFailed('解码错误')
               except jwt.InvalidTokenError:
                   raise AuthenticationFailed('不合法的token')
               user=self.authenticate_credentials(payload)
               return (user, token)
    -自定制认证类的使用方式:
  -全局使用
       -局部使用

3 base64编码(跟语言无关,跟框架无关)
-不同语言的base64可以相互编码解码
   -base64内置模块
   -图片的二进制,有的时候也会以base64的形式编码
   
4 drf的视图(两个,5个,9个,视图集)+序列化器+自动生成路由

1 基于jwt的多方式登陆

1 手机号+密码   用户名+密码  邮箱+密码
2 流程分析(post请求):
-路由:自动生成
   -视图类:ViewSet(ViewSetMixin, views.APIView)
   -序列化类:重写validate方法,在这里面对用户名和密码进行校验
   
3 代码实现

路由

path('login/', views.LoginViewSet.as_view({'post':'create'})),

视图

class LoginViewSet(ViewSet):
   def create(self, request, *args, **kwargs):
       # 实例化得到一个序列化类的对象
       # ser=LoginSerializer(data=request.data,context={'request':request})
       ser = LoginSerializer(data=request.data)
       # 序列化类的对象的校验方法
       ser.is_valid(raise_exception=True)  # 字段自己的校验,局部钩子校验,全局钩子校验
       # 如果通过,表示登录成功,返回手动签发的token
       token = ser.context.get('token')
       username = ser.context.get('username')
       return APIResponse(token=token, username=username)
       # 如果失败,不用管了

序列化类

from rest_framework import serializers
from app01.models import UserInfo
import re
from rest_framework.exceptions import ValidationError
from rest_framework_jwt.utils import jwt_encode_handler, jwt_payload_handler
from rest_framework_jwt.views import obtain_jwt_token

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

   class Meta:
       model = UserInfo
       fields = ['username', 'password']

   def validate(self, attrs):
       # username可能是邮箱,手机号,用户名
       username = attrs.get('username')
       password = attrs.get('password')
       # 如果是手机号
       if re.match('^1[3-9]\d{9}$', username):
           # 以手机号登录
           user = UserInfo.objects.filter(phone=username).first()
       elif re.match('^.+@.+$', username):
           # 以邮箱登录
           user = UserInfo.objects.filter(email=username).first()
       else:
           # 以用户名登录
           user = UserInfo.objects.filter(username=username).first()
       # 如果user有值并且密码正确
       if user and user.check_password(password):
           # 登录成功,生成token
           # drf-jwt中有通过user对象生成token的方法
           payload = jwt_payload_handler(user)
           token = jwt_encode_handler(payload)
           # token是要在视图类中使用,现在我们在序列化类中
           # self.context.get('request')
           # 视图类和序列化类之间通过context这个字典来传递数据
           self.context['token'] = token
           self.context['username'] = user.username
           return attrs

       else:
           raise ValidationError('用户名或密码错误')

2 自定义user表,签发token,认证类

表模型

class MyUser(models.Model):
   username = models.CharField(max_length=32)
   password = models.CharField(max_length=32)
   phone = models.CharField(max_length=32)
   email = models.EmailField()

路由

path('login2/', views.MyLoginView.as_view()),

视图

jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
from rest_framework_jwt.views import obtain_jwt_token


class MyLoginView(APIView):
   def post(self, request, *args, **kwargs):
       username = request.data.get('username')
       password = request.data.get('password')
       # 如果是手机号
       if re.match('^1[3-9]\d{9}$', username):
           # 以手机号登录
           user = MyUser.objects.filter(phone=username).first()
       elif re.match('^.+@.+$', username):
           # 以邮箱登录
           user = MyUser.objects.filter(email=username).first()
       else:
           # 以用户名登录
           user = MyUser.objects.filter(username=username).first()
       # 如果user有值并且密码正确
       if user and user.password == password:
           # 登录成功,生成token
           # drf-jwt中有通过user对象生成token的方法
           payload = jwt_payload_handler(user)
           token = jwt_encode_handler(payload)
           return APIResponse(token=token, username=user.username)
       else:
           return APIResponse(code=101, msg='用户名或密码错误')

3 book,publish,author表关系及抽象表建立

# 注意:以后所有的数据删除,尽量用软删除,使用一个字段标志是否删除,而不是真正的从数据库中删除
-好处:1 这样删除数据不会影响索引,不会导致索引失效
   2 之前存的用户数据还在,以备以后使用
# 表模型如下
# 抽象出一个基表(不再数据库生成,abstract=True),只用来继承

class BaseModel(models.Model):
   is_delete = models.BooleanField(default=False)
   create_time = models.DateTimeField(auto_now_add=True)

   class Meta:
       # 基表必须设置abstract,基表就是给普通Model类继承使用的,设置了abstract就不会完成数据库迁移完成建表
       abstract = True


class Book(BaseModel):
   name = models.CharField(max_length=16)
   price = models.DecimalField(max_digits=5, decimal_places=2)
   publish = models.ForeignKey(to='Publish', db_constraint=False, on_delete=models.DO_NOTHING)
   # 重点:多对多外键实际在关系表中,ORM默认关系表中两个外键都是级联
   # ManyToManyField字段不提供设置on_delete,如果想设置关系表级联,只能手动定义关系表
   authors = models.ManyToManyField(to='Author', related_name='books', db_constraint=False)

   @property
   def publish_name(self):
       return self.publish.name
   @property
   def author_list(self):
       # ll=[]
       # for author in self.authors.all():
       #     ll.append({'name':author.name,'sex':author.get_sex_display()})
       return [{'name': author.name, 'sex': author.get_sex_display()} for author in self.authors.all()]


class Publish(BaseModel):
   name = models.CharField(max_length=16)
   address = models.CharField(max_length=64)


class Author(BaseModel):
   name = models.CharField(max_length=16)
   sex = models.IntegerField(choices=[(0, '男'), (1, '女')], default=0)


class AuthorDetail(BaseModel):
   mobile = models.CharField(max_length=11)
   # 有作者可以没有详情,删除作者,详情一定会被级联删除
   # 外键字段为正向查询字段,related_name是反向查询字段
   author = models.OneToOneField(to='Author', related_name='detail', db_constraint=False, on_delete=models.CASCADE)

4 book表单增群增

class BookView(APIView):
   def post(self, request, *args, **kwargs):
       if isinstance(request.data, dict):
           # 增一条
           ser = serializer.BookSerializer(data=request.data)
           ser.is_valid(raise_exception=True)
           ser.save()
           return APIResponse(data=ser.data)
       elif isinstance(request.data, list):
           # 增多条
           ser = serializer.BookSerializer(data=request.data, many=True)
           # 内部如何实现的?
           # many=True,ser不是BookSerializer对象,而是ListSerializer对象,套了一个个的BookSerializer
           print(type(ser))
           ser.is_valid(raise_exception=True)
           #
           from rest_framework.serializers import ListSerializer
           ser.save()  # ListSerializer的save
           return APIResponse(msg='增加%s条成功' % len(request.data))

5 book表单查群查

class BookView(APIView):
   def get(self, request, *args, **kwargs):
       pk = kwargs.get('pk', None)
       if pk:
           # 单查
           # 方式一
           # book=models.Book.objects.filter(id=pk).filter(is_delete=False).first()
           # if not book:
           #     raise Exception('要查询的不存在')

           # 方式二
           book = models.Book.objects.get(id=pk, is_delete=False)
           ser = serializer.BookSerializer(instance=book)

       else:
           # 查所有
           book_list = models.Book.objects.all().filter(is_delete=False)
           ser = serializer.BookSerializer(instance=book_list, many=True)
       return APIResponse(data=ser.data)

6 book表单改群改

class BookView(APIView):
   def put(self, request, *args, **kwargs):
       pk = kwargs.get('pk', None)
       if pk:
           # 单条修改
           book = models.Book.objects.get(id=pk, is_delete=False)
           ser = serializer.BookSerializer(instance=book, data=request.data)
           ser.is_valid(raise_exception=True)
           ser.save()
           return APIResponse(msg='修改成功')
       else:
           # 分析:ListSerializer的update方法没有写,需要我们自己写
           from rest_framework.serializers import ListSerializer
           # pks=[item['id'] for item in request.data]

           # 如果不重写ListSerializer的update方法,这是存不进去的
           pks = []
           for item in request.data:
               pks.append(item['id'])
               item.pop('id')

           print(request.data)
           book_list = models.Book.objects.filter(id__in=pks, is_delete=False)
           ser = serializer.BookSerializer(instance=book_list, data=request.data, many=True)
           print(type(ser))
           ser.is_valid(raise_exception=True)
           ser.save()
           return APIResponse(msg='修改%s条成功')

           # 你们能想到的方法
           # pks = []
           # for item in request.data:
           #     pks.append(item['id'])
           #     item.pop('id')
           # book_list = models.Book.objects.filter(id__in=pks, is_delete=False)
           #
           # for i,book in enumerate(book_list):
           #     ser = serializer.BookSerializer(instance=book, data=request.data[i])
           #     ser.is_valid(raise_exception=True)
           #     ser.save()
           # return APIResponse(msg='修改%s条成功'%len(book_list))

7 book表的单删群删

class BookView(APIView):

   def delete(self, request, *args, **kwargs):
       pk = kwargs.get('pk', None)
       pks = []
       if pk:
           # 单条删除
           # res=models.Book.objects.filter(id=pk).update(is_delete=True)
           # print(res)
           # return APIResponse(msg='删除成功')
           pks.append(pk)
       else:
           pks = request.data

       res = models.Book.objects.filter(id__in=pks).update(is_delete=True)
       if res >= 1:
           return APIResponse(msg='删除%s条成功' % res)
       else:
           # raise Exception('没有要删除的数据')
           return APIResponse(code=999, msg='没有要删除的数据')

8 序列化类

from app01 import models


class ListBookSerializer(serializers.ListSerializer):
   # def create(self, validated_data):
   #     print('=======',validated_data)
   #     return '1'
   def update(self, instance, validated_data):
       print(instance) # book_list:是一堆图书对象
       print(validated_data) # 列表套字典,是要修改的数据

       return [self.child.update(book, validated_data[i]) for i, book in enumerate(instance)]


class BookSerializer(serializers.ModelSerializer):
   class Meta:
       model = models.Book
       list_serializer_class=ListBookSerializer # 指定many=True的时候,生成的ListBookSerializer的对象了
       fields = ['name', 'price', 'publish', 'authors', 'publish_name', 'author_list']
       extra_kwargs = {
           'publish': {'write_only': True},
           'authors': {'write_only': True},
           'publish_name': {'read_only': True},
           'author_list': {'read_only': True},

      }

   # def create(self, validated_data):
   #     print(validated_data)

9 路由

path('books/', views.BookView.as_view()),
re_path('books/(?P<pk>\d+)', views.BookView.as_view()),
posted @ 2021-01-21 21:19  投降输一半!  阅读(503)  评论(0编辑  收藏  举报