2020-12-14 drf之jwt认证

昨日回顾

1 分页功能

-三个类:普通分页,偏移分页,游标分页
-每个类中都有几个属性:查询的字段,每页显示的条数,每页最多显示的条数,游标分页中有个排序
-定义一个类,继承上面3个其中一个,重写字段
-继承了APIView:实例化得到分页对象,把要分页的数据传入,返回分页后的数据,序列化,可以按照自己定制的规则返回,也可也使用page.get_paginated_response(ser.data)
-如果继承了ListModelMixin和GenericAPIView,直接配置就可以了
    pagination_class = MyCursorPagination

2 全局异常

-写一个方法
def common_exception_handler(exc, context):
    response = exception_handler(exc, context)
    if response is None:
        response = Response({'code': 999, 'detail': str(exc)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
    return response
-setting中配置一下
    'EXCEPTION_HANDLER':'app01.utils.common_exception_handler'
-通常情况咱们会记录日志
    -使用django日志记录  xx.log文件中
    -使用sentry(公司自己写)日志记录, 平台(django),查询,统计,告警

3 自己写的Response

class APIResponse(Response):
    def __init__(self, code=100, msg='成功', data=None, status=None, headers=None, content_type=None, **kwargs):
        dic = {'code': code, 'msg': msg}
        if data:
            dic['data'] = data
        dic.update(kwargs) # 这里使用update
        super().__init__(data=dic, status=status,
                         template_name=None, headers=headers,
                         exception=False, content_type=content_type)
#我们期望这种格式
{
    code:100
    msg:成功,失败信息
    data:[]
    count:
}
# 使用
APIResponse(data=[],code=101,heades={})

4 自动生成接口文档

-手写
-自动生成(drf:crorapi),swagger(java,go,python)
    -安装:pip3 install coreapi
    -路由中配置:path('docs/', include_docs_urls(title='图书管理系统api')),
    -在配置文件中配置:'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.coreapi.AutoSchema',
    -写视图类,在里面要加注释,就在接口平台就能看到

1、作业讲解之用户注册/查询用户/修改头像

详见当天代码,补上来

作业:

1 自定义User表扩展auth的User,新增mobile唯一约束字段;新增icon图片字段
2 在自定义User表基础上,用 GenericViewSet + CreateModelMixin + serializer 完成User表新增接口(就是注册接口)(重要提示:序列化类要重写create方法,不然密码就是明文了)
3 在自定义User表基础上,用 GenericViewSet + RetrieveModelMixin + serializer 完成User表单查(就是用户中心)
4 在自定义User表基础上,用 GenericViewSet + UpdateModelMixin + serializer 完成用户头像的修改

代码:

models.py 扩写了user表

from django.contrib.auth.models import AbstractUser

class UserInfo(AbstractUser):
    mobile=models.CharField(max_length=32,unique=True)
    icon=models.ImageField(upload_to='head/',default='head/default.png')

serializer.py 可以写多个序列化器来对一个视图类

from rest_framework import serializers
from homework import models
from rest_framework.exceptions import ValidationError


class UserModelSerializer(serializers.ModelSerializer):
    # 注册功能,需要什么字段
    # username,password,re_password,mobile
    # 这个字段在表中没有,需要写成write_only=True
    re_password = serializers.CharField(max_length=18, min_length=3, write_only=True)

    class Meta:
        model = models.UserInfo
        fields = ['username', 'password', 'mobile', 're_password', 'icon']

        extra_kwargs = {
            'username': {'max_length': 12, 'min_length': 3},
            'password': {'write_only': True},
            'icon': {'read_only': True}
        }

    # 写mobile的局部钩子

    def validate_mobile(self, data):
        if len(data) == 11:
            return data
        else:
            raise ValidationError('手机号不合法')

    # 全局钩子校验两次密码是否一致

    def validate(self, attrs):
        password = attrs.get('password')
        re_password = attrs.get('re_password')
        if password == re_password:
            return attrs
        else:
            raise ValidationError('两次密码不一致')

    # 重写create方法,实现密码的加密

    def create(self, validated_data):
        # re_password不在表字段里,在这里移除
        validated_data.pop('re_password')
        # models.UserInfo.objects.create(**validated_data) # 密码是明文
        user = models.UserInfo.objects.create_user(**validated_data)
        return user  # 不要忘记了这句话


# 序列化的时候,一个模型类,不一定对着一个序列化类
# 这个序列化类只做序列化用
class UserReadOnlyModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.UserInfo
        fields = ['username', 'mobile', 'icon', 'email', 'date_joined']


# 这个序列化类,只做修改头像用
class UserIconModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.UserInfo
        fields = ['icon']

views.py 一个视图类可以对应不同的序列化器类,只要重写get_serializer_class(self):方法

from homework import models
from homework.serializer import UserModelSerializer, UserReadOnlyModelSerializer, UserIconModelSerializer
from rest_framework.mixins import CreateModelMixin, RetrieveModelMixin, UpdateModelMixin
from rest_framework.generics import GenericAPIView
from rest_framework.viewsets import GenericViewSet, ViewSetMixin


# ViewSetMicin:路由写法变了,自动生成路由
# GenericAPIView :必须指定queryset,和serializer

class UserView(GenericViewSet, CreateModelMixin, RetrieveModelMixin, UpdateModelMixin):
    # class UserView(ViewSetMixin,GenericAPIView,CreateModelMixin):
    queryset = models.UserInfo.objects.all()
    serializer_class = UserModelSerializer

    # 重写get_serializer_class(self):方法,实现不同的请求,返回的序列化类不一样,这个方法返回的是哪个序列化器就回用哪个序列化器来执行
    def get_serializer_class(self):
        # 可以根据请求方式来选择哪个序列化器来执行
        if self.action == 'create':
            return UserModelSerializer
        elif self.action == 'retrieve':
            return UserReadOnlyModelSerializer
        elif self.action == 'update':
            return UserIconModelSerializer


# 是再写一个视图类,还是继续用上面的?继续用上面的
class UserReadOnlyView(GenericViewSet, RetrieveModelMixin):
    queryset = models.UserInfo.objects.all()
    serializer_class = UserReadOnlyModelSerializer

jwt的使用,登录以后才能访问该视图类

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework_jwt.authentication import JSONWebTokenAuthentication
from rest_framework.permissions import IsAuthenticated


#登录后才能访问,内置jwt的认证类
class OrderView(APIView):
    # 只配它不行,不管是否登录,都能访问,需要搭配一个内置权限类
    authentication_classes = [JSONWebTokenAuthentication]
    permission_classes = [IsAuthenticated]  # 必须要带一个自带的权限认证,否则并没有通过认证,但是不会报错,但是request.user里没有数据

    def get(self, request):
         print(request.user.username)
         return Response('订单数据')


#自定义基于jwt的认证类
from homework.auth import JwtAuthentication
class OrderView(APIView):
    # 登录以后才能访问
    authentication_classes = [JwtAuthentication]

    def get(self, request):
        print(request.user.username)
        return Response('订单数据')

2、jwt认证介绍

-1、不在使用Session认证机制,而使用Json Web Token(本质就是token)认证机制,用户登录认证
-2、用户只要登录了,返回用户一个token串(随机字符串),每次用户发请求,需要携带这个串过来,验证通过,我们认为用户登录了
-3、JWT的构成(字符串)
    -三部分(每一部分中间通过.分割):header   payload  signature
    -header:明类型,这里是jwt,声明加密算法,头里加入公司信息...,base64转码
    {
      'typ': 'JWT',
      'alg': 'HS256'
    }
    -payload:荷载(有用),当前用户的信息(用户名,id,这个token的过期时间,手机号),base64转码
        {
          "sub": "1234567898",
          "name": "egon",
          "admin": true,
          "userid":1,
          'mobile':123444444
        }
    -signature:签名
        -把前面两部分的内容通过加密算法+密钥加密后得到的一个字符串

    -jwt总的构成样子:
        eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

-4、JWT认证原理
    -用户携带用户名、密码登录我的系统,校验通过,生成一个token(三部分),返回给用户---》登录功能完成
    -访问需要登录的接口(用户中心),必须携带token过来,后端拿到token后,把header和payload截取出来,再通过一样的加密方式和密码得到一个signature,
    和该token的signature比较,如果一样,表示是正常的token,就可以继续往后访问

3、base64价绍和使用

-1、任何语言都有base64的加码和解码,转码方式(加密方式)
-2、 python中base64的加密与解密
   import base64

    import json
    dic_info={
      "name": "lqz",
      "age": 18
    }
    # 转成json格式字符串

    dic_str=json.dumps(dic_info)
    print(dic_str)
    #eyJuYW1lIjogImxxeiIsICJhZ2UiOiAxOH0=
    #eyJuYW1lIjogImxxeiIsICJhZ2UiOiAxOH0=
    # 需要用bytes格式
    # 加码
    base64_str=base64.b64encode(dic_str.encode('utf-8'))
    print(base64_str)


    # 解码

    res_bytes=base64.b64decode('eyJuYW1lIjogImxxeiIsICJhZ2UiOiAxOH0=')
    print(res_bytes)

4、jwt基本使用(jwt内置,控制用户登录后能访问和未登陆能访问)

-1、drf中使用jwt,借助第三方模块:https://github.com/jpadilla/django-rest-framework-jwt
-2、pip3 install djangorestframework-jwt
-3、快速使用(默认使用auth的user表)
    -1、在默认auth的user表中创建一个用户
    -2、在路由中配置
        # jwt自带的登录视图,最简单的使用
        from rest_framework_jwt.views import obtain_jwt_token
        path('login/', obtain_jwt_token)。这个是jwt自带的登录视图
    -3、用postman向这个地址发送post请求,携带用户名,密码,登录成功就会返回token
    -4、obtain_jwt_token本质也是一个视图类,继承了APIView
        -通过前端传入的用户名密码,校验用户,如果校验通过,生成token,返回
        -如果检验失败,返回错误信息
-4、用户登录以后才能访问某个接口
    -jwt模块内置了认证类,拿过来局部配置就可以
    -class OrderView(APIView):
        # 只配它不行,不管是否登录,都能范围,需要搭配一个内置权限类
        authentication_classes = [JSONWebTokenAuthentication, ]
        permission_classes = [IsAuthenticated,]
        def get(self, request):
            print(request.user.username)
            return Response('订单的数据')
-5、用户未登录,就能访问,区别看第六条
    -class OrderView(APIView):
        # 只配它不行,不管是否登录,都能范围,需要搭配一个内置权限类
        authentication_classes = [JSONWebTokenAuthentication, ]
        def get(self, request):
            print(request.user.username)
            return Response('订单的数据')
-6、如果用户携带了token,并且配置了JSONWebTokenAuthentication,从request.user就能拿到当前登录用户,如果没有携带,当前登录用户就是匿名用户,
request.user里没有东西,这个jwt的认证不是我们传统的认证,只是request.user里有无用户信息。

-7、前端要发送请求,携带jwt,格式必须如下
    -把token放到请求头中,key为:Authorization
    jwt加空格
    -value必须为:jwt eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjo1LCJ1c2VybmFtZSI6ImVnb24xIiwiZXhwIjoxNjA1MjQxMDQzLCJlbWFpbCI6IiJ9.7Y3PQM0imuSBc8CUe_h-Oj-2stdyzXb_U-TEw-F82WE

5、控制登录接口返回的数据格式

-1 控制登录接口返回的数据格式如下
    {
    code:100
    msg:登录成功
    token:asdfasfd
    username:egon
    }

-2 写一个函数
    from homework.serializer import UserReadOnlyModelSerializer
    def jwt_response_payload_handler(token, user=None, request=None):
        return {'code': 100,
                'msg': '登录成功',
                'token': token,
                'user': UserReadOnlyModelSerializer(instance=user).data
                }
-3 在setting.py中配置
    import datetime
    JWT_AUTH = {
        # 过期时间
    'JWT_EXPIRATION_DELTA': datetime.timedelta(days=365*7),
    # 自定义认证结果:见函数字典中序列化user和自定义response
    # 如果不自定义,返回的格式是固定的,只有token字段
        'JWT_RESPONSE_PAYLOAD_HANDLER': 'homework.utils.jwt_response_payload_handler',
    }

6、自定义基于jwt的认证类

-1、自己实现基于jwt的认证类,通过认证,才能访问,通不过认证就返回错误信息
-2、代码如下
from rest_framework.exceptions import AuthenticationFailed
from rest_framework_jwt.utils import jwt_decode_handler
from rest_framework_jwt.authentication import BaseJSONWebTokenAuthentication  # 这是jwt里内置的认证的认证父类
   class JwtAuthentication(BaseJSONWebTokenAuthentication):
        def authenticate(self, request):
            # 认证逻辑()
            # token信息可以放在请求头中,请求地址中
            # key值可以随意叫
            # token=request.GET.get('token')
            token=request.META.get('HTTP_Authorization'.upper()) # 放在Header头里
            # 校验token是否合法
            try:
                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 在视图类中配置
    authentication_classes = [JwtAuthentication, ]
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,496评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,407评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,632评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,180评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,198评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,165评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,052评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,910评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,324评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,542评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,711评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,424评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,017评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,668评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,823评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,722评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,611评论 2 353

推荐阅读更多精彩内容

  • Migrate不生成表 makemigrations 和 migrate ,新加的这两个实体无法生成表。 1, 删...
    7d4b0b51c9d4阅读 889评论 0 0
  • 1. 版本 了解* 可以放在url,这种是比较推荐的。它需要配套的路由,一般所有的CBV都要使用,所以放在全局配...
    上帝大人阅读 164评论 0 1
  • 久违的晴天,家长会。 家长大会开好到教室时,离放学已经没多少时间了。班主任说已经安排了三个家长分享经验。 放学铃声...
    飘雪儿5阅读 7,520评论 16 22
  • 今天感恩节哎,感谢一直在我身边的亲朋好友。感恩相遇!感恩不离不弃。 中午开了第一次的党会,身份的转变要...
    迷月闪星情阅读 10,562评论 0 11
  • 可爱进取,孤独成精。努力飞翔,天堂翱翔。战争美好,孤独进取。胆大飞翔,成就辉煌。努力进取,遥望,和谐家园。可爱游走...
    赵原野阅读 2,726评论 1 1