zoukankan      html  css  js  c++  java
  • 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()),
  • 相关阅读:
    程序员的数学基础课
    程序员的数学基础课
    Ruby 自学记录 7
    今日新闻整理 2020-7-22
    github of Ruby developers
    Ruby 自学记录 6 create a new controller action and view
    Ruby 自学记录 5 Using RubyMine that I quickly create Rails project
    Kafka基础(十三): 基本面试题(三)
    Hadoop基础(五十六):MapReduce 过程详解 (用WordCount作为例子)
    Hive面试题(2):Hive 执行过程实例分析
  • 原文地址:https://www.cnblogs.com/usherwang/p/14310481.html
Copyright © 2011-2022 走看看