2020-12-14 drf之jwt認(rèn)證

昨日回顧

1 分頁功能

-三個(gè)類:普通分頁,偏移分頁,游標(biāo)分頁
-每個(gè)類中都有幾個(gè)屬性:查詢的字段捆昏,每頁顯示的條數(shù),每頁最多顯示的條數(shù)吹艇,游標(biāo)分頁中有個(gè)排序
-定義一個(gè)類诱建,繼承上面3個(gè)其中一個(gè),重寫字段
-繼承了APIView:實(shí)例化得到分頁對象仅颇,把要分頁的數(shù)據(jù)傳入单默,返回分頁后的數(shù)據(jù),序列化忘瓦,可以按照自己定制的規(guī)則返回搁廓,也可也使用page.get_paginated_response(ser.data)
-如果繼承了ListModelMixin和GenericAPIView,直接配置就可以了
    pagination_class = MyCursorPagination

2 全局異常

-寫一個(gè)方法
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'
-通常情況咱們會(huì)記錄日志
    -使用django日志記錄  xx.log文件中
    -使用sentry(公司自己寫)日志記錄耕皮, 平臺(tái)(django)境蜕,查詢,統(tǒng)計(jì)凌停,告警

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 自動(dòng)生成接口文檔

-手寫
-自動(dòng)生成(drf:crorapi)罚拟,swagger(java台诗,go完箩,python)
    -安裝:pip3 install coreapi
    -路由中配置:path('docs/', include_docs_urls(title='圖書管理系統(tǒng)api')),
    -在配置文件中配置:'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.coreapi.AutoSchema',
    -寫視圖類,在里面要加注釋拉队,就在接口平臺(tái)就能看到

1弊知、作業(yè)講解之用戶注冊/查詢用戶/修改頭像

詳見當(dāng)天代碼,補(bǔ)上來

作業(yè):

1 自定義User表擴(kuò)展auth的User粱快,新增mobile唯一約束字段秩彤;新增icon圖片字段
2 在自定義User表基礎(chǔ)上,用 GenericViewSet + CreateModelMixin + serializer 完成User表新增接口(就是注冊接口)(重要提示:序列化類要重寫create方法皆尔,不然密碼就是明文了)
3 在自定義User表基礎(chǔ)上呐舔,用 GenericViewSet + RetrieveModelMixin + serializer 完成User表單查(就是用戶中心)
4 在自定義User表基礎(chǔ)上,用 GenericViewSet + UpdateModelMixin + serializer 完成用戶頭像的修改

代碼:

models.py 擴(kuò)寫了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 可以寫多個(gè)序列化器來對一個(gè)視圖類

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


class UserModelSerializer(serializers.ModelSerializer):
    # 注冊功能慷蠕,需要什么字段
    # username,password,re_password,mobile
    # 這個(gè)字段在表中沒有珊拼,需要寫成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('手機(jī)號(hào)不合法')

    # 全局鉤子校驗(yàn)兩次密碼是否一致

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

    # 重寫create方法,實(shí)現(xiàn)密碼的加密

    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  # 不要忘記了這句話


# 序列化的時(shí)候澎现,一個(gè)模型類,不一定對著一個(gè)序列化類
# 這個(gè)序列化類只做序列化用
class UserReadOnlyModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.UserInfo
        fields = ['username', 'mobile', 'icon', 'email', 'date_joined']


# 這個(gè)序列化類每辟,只做修改頭像用
class UserIconModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.UserInfo
        fields = ['icon']

views.py 一個(gè)視圖類可以對應(yīng)不同的序列化器類剑辫,只要重寫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:路由寫法變了,自動(dòng)生成路由
# 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):方法妹蔽,實(shí)現(xiàn)不同的請求,返回的序列化類不一樣挠将,這個(gè)方法返回的是哪個(gè)序列化器就回用哪個(gè)序列化器來執(zhí)行
    def get_serializer_class(self):
        # 可以根據(jù)請求方式來選擇哪個(gè)序列化器來執(zhí)行
        if self.action == 'create':
            return UserModelSerializer
        elif self.action == 'retrieve':
            return UserReadOnlyModelSerializer
        elif self.action == 'update':
            return UserIconModelSerializer


# 是再寫一個(gè)視圖類胳岂,還是繼續(xù)用上面的?繼續(xù)用上面的
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


#登錄后才能訪問乳丰,內(nèi)置jwt的認(rèn)證類
class OrderView(APIView):
    # 只配它不行,不管是否登錄内贮,都能訪問产园,需要搭配一個(gè)內(nèi)置權(quán)限類
    authentication_classes = [JSONWebTokenAuthentication]
    permission_classes = [IsAuthenticated]  # 必須要帶一個(gè)自帶的權(quán)限認(rèn)證,否則并沒有通過認(rèn)證夜郁,但是不會(huì)報(bào)錯(cuò)什燕,但是request.user里沒有數(shù)據(jù)

    def get(self, request):
         print(request.user.username)
         return Response('訂單數(shù)據(jù)')


#自定義基于jwt的認(rèn)證類
from homework.auth import JwtAuthentication
class OrderView(APIView):
    # 登錄以后才能訪問
    authentication_classes = [JwtAuthentication]

    def get(self, request):
        print(request.user.username)
        return Response('訂單數(shù)據(jù)')

2、jwt認(rèn)證介紹

-1竞端、不在使用Session認(rèn)證機(jī)制秋冰,而使用Json Web Token(本質(zhì)就是token)認(rèn)證機(jī)制,用戶登錄認(rèn)證
-2婶熬、用戶只要登錄了剑勾,返回用戶一個(gè)token串(隨機(jī)字符串)埃撵,每次用戶發(fā)請求,需要攜帶這個(gè)串過來虽另,驗(yàn)證通過暂刘,我們認(rèn)為用戶登錄了
-3、JWT的構(gòu)成(字符串)
    -三部分(每一部分中間通過.分割):header   payload  signature
    -header:明類型捂刺,這里是jwt,聲明加密算法谣拣,頭里加入公司信息...,base64轉(zhuǎn)碼
    {
      'typ': 'JWT',
      'alg': 'HS256'
    }
    -payload:荷載(有用),當(dāng)前用戶的信息(用戶名族展,id森缠,這個(gè)token的過期時(shí)間,手機(jī)號(hào))仪缸,base64轉(zhuǎn)碼
        {
          "sub": "1234567898",
          "name": "egon",
          "admin": true,
          "userid":1,
          'mobile':123444444
        }
    -signature:簽名
        -把前面兩部分的內(nèi)容通過加密算法+密鑰加密后得到的一個(gè)字符串

    -jwt總的構(gòu)成樣子:
        eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

-4贵涵、JWT認(rèn)證原理
    -用戶攜帶用戶名、密碼登錄我的系統(tǒng)恰画,校驗(yàn)通過宾茂,生成一個(gè)token(三部分),返回給用戶---》登錄功能完成
    -訪問需要登錄的接口(用戶中心)拴还,必須攜帶token過來跨晴,后端拿到token后,把header和payload截取出來片林,再通過一樣的加密方式和密碼得到一個(gè)signature端盆,
    和該token的signature比較,如果一樣费封,表示是正常的token爱谁,就可以繼續(xù)往后訪問

3、base64價(jià)紹和使用

-1孝偎、任何語言都有base64的加碼和解碼,轉(zhuǎn)碼方式(加密方式)
-2凉敲、 python中base64的加密與解密
   import base64

    import json
    dic_info={
      "name": "lqz",
      "age": 18
    }
    # 轉(zhuǎn)成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內(nèi)置,控制用戶登錄后能訪問和未登陸能訪問)

-1爷抓、drf中使用jwt势决,借助第三方模塊:https://github.com/jpadilla/django-rest-framework-jwt
-2、pip3 install djangorestframework-jwt
-3蓝撇、快速使用(默認(rèn)使用auth的user表)
    -1果复、在默認(rèn)auth的user表中創(chuàng)建一個(gè)用戶
    -2、在路由中配置
        # jwt自帶的登錄視圖渤昌,最簡單的使用
        from rest_framework_jwt.views import obtain_jwt_token
        path('login/', obtain_jwt_token)虽抄。這個(gè)是jwt自帶的登錄視圖
    -3走搁、用postman向這個(gè)地址發(fā)送post請求,攜帶用戶名迈窟,密碼私植,登錄成功就會(huì)返回token
    -4、obtain_jwt_token本質(zhì)也是一個(gè)視圖類车酣,繼承了APIView
        -通過前端傳入的用戶名密碼曲稼,校驗(yàn)用戶,如果校驗(yàn)通過湖员,生成token贫悄,返回
        -如果檢驗(yàn)失敗,返回錯(cuò)誤信息
-4娘摔、用戶登錄以后才能訪問某個(gè)接口
    -jwt模塊內(nèi)置了認(rèn)證類窄坦,拿過來局部配置就可以
    -class OrderView(APIView):
        # 只配它不行,不管是否登錄,都能范圍,需要搭配一個(gè)內(nèi)置權(quán)限類
        authentication_classes = [JSONWebTokenAuthentication, ]
        permission_classes = [IsAuthenticated,]
        def get(self, request):
            print(request.user.username)
            return Response('訂單的數(shù)據(jù)')
-5、用戶未登錄晰筛,就能訪問嫡丙,區(qū)別看第六條
    -class OrderView(APIView):
        # 只配它不行,不管是否登錄,都能范圍,需要搭配一個(gè)內(nèi)置權(quán)限類
        authentication_classes = [JSONWebTokenAuthentication, ]
        def get(self, request):
            print(request.user.username)
            return Response('訂單的數(shù)據(jù)')
-6、如果用戶攜帶了token读第,并且配置了JSONWebTokenAuthentication曙博,從request.user就能拿到當(dāng)前登錄用戶,如果沒有攜帶怜瞒,當(dāng)前登錄用戶就是匿名用戶父泳,
request.user里沒有東西,這個(gè)jwt的認(rèn)證不是我們傳統(tǒng)的認(rèn)證吴汪,只是request.user里有無用戶信息惠窄。

-7、前端要發(fā)送請求漾橙,攜帶jwt杆融,格式必須如下
    -把token放到請求頭中,key為:Authorization
    jwt加空格
    -value必須為:jwt eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjo1LCJ1c2VybmFtZSI6ImVnb24xIiwiZXhwIjoxNjA1MjQxMDQzLCJlbWFpbCI6IiJ9.7Y3PQM0imuSBc8CUe_h-Oj-2stdyzXb_U-TEw-F82WE

5霜运、控制登錄接口返回的數(shù)據(jù)格式

-1 控制登錄接口返回的數(shù)據(jù)格式如下
    {
    code:100
    msg:登錄成功
    token:asdfasfd
    username:egon
    }

-2 寫一個(gè)函數(shù)
    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 = {
        # 過期時(shí)間
    'JWT_EXPIRATION_DELTA': datetime.timedelta(days=365*7),
    # 自定義認(rèn)證結(jié)果:見函數(shù)字典中序列化user和自定義response
    # 如果不自定義择镇,返回的格式是固定的报破,只有token字段
        'JWT_RESPONSE_PAYLOAD_HANDLER': 'homework.utils.jwt_response_payload_handler',
    }

6立宜、自定義基于jwt的認(rèn)證類

-1舆瘪、自己實(shí)現(xiàn)基于jwt的認(rèn)證類,通過認(rèn)證焦除,才能訪問激况,通不過認(rèn)證就返回錯(cuò)誤信息
-2、代碼如下
from rest_framework.exceptions import AuthenticationFailed
from rest_framework_jwt.utils import jwt_decode_handler
from rest_framework_jwt.authentication import BaseJSONWebTokenAuthentication  # 這是jwt里內(nèi)置的認(rèn)證的認(rèn)證父類
   class JwtAuthentication(BaseJSONWebTokenAuthentication):
        def authenticate(self, request):
            # 認(rèn)證邏輯()
            # token信息可以放在請求頭中,請求地址中
            # key值可以隨意叫
            # token=request.GET.get('token')
            token=request.META.get('HTTP_Authorization'.upper()) # 放在Header頭里
            # 校驗(yàn)token是否合法
            try:
                payload = jwt_decode_handler(token)
            except jwt.ExpiredSignature:
                raise AuthenticationFailed('過期了')
            except jwt.DecodeError:
                raise AuthenticationFailed('解碼錯(cuò)誤')
            except jwt.InvalidTokenError:
                raise AuthenticationFailed('不合法的token')
            user=self.authenticate_credentials(payload)
            return (user, token)
-3 在視圖類中配置
    authentication_classes = [JwtAuthentication, ]
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市乌逐,隨后出現(xiàn)的幾起案子竭讳,更是在濱河造成了極大的恐慌,老刑警劉巖黔帕,帶你破解...
    沈念sama閱讀 221,273評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件代咸,死亡現(xiàn)場離奇詭異,居然都是意外死亡成黄,警方通過查閱死者的電腦和手機(jī)呐芥,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,349評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來奋岁,“玉大人思瘟,你說我怎么就攤上這事∥帕妫” “怎么了滨攻?”我有些...
    開封第一講書人閱讀 167,709評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長蓝翰。 經(jīng)常有香客問我光绕,道長,這世上最難降的妖魔是什么畜份? 我笑而不...
    開封第一講書人閱讀 59,520評(píng)論 1 296
  • 正文 為了忘掉前任诞帐,我火速辦了婚禮,結(jié)果婚禮上爆雹,老公的妹妹穿的比我還像新娘停蕉。我一直安慰自己,他們只是感情好钙态,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,515評(píng)論 6 397
  • 文/花漫 我一把揭開白布慧起。 她就那樣靜靜地躺著,像睡著了一般册倒。 火紅的嫁衣襯著肌膚如雪蚓挤。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,158評(píng)論 1 308
  • 那天驻子,我揣著相機(jī)與錄音灿意,去河邊找鬼。 笑死拴孤,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的甲捏。 我是一名探鬼主播演熟,決...
    沈念sama閱讀 40,755評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了芒粹?” 一聲冷哼從身側(cè)響起兄纺,我...
    開封第一講書人閱讀 39,660評(píng)論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎化漆,沒想到半個(gè)月后估脆,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,203評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡座云,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,287評(píng)論 3 340
  • 正文 我和宋清朗相戀三年疙赠,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片朦拖。...
    茶點(diǎn)故事閱讀 40,427評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡圃阳,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出璧帝,到底是詐尸還是另有隱情捍岳,我是刑警寧澤,帶...
    沈念sama閱讀 36,122評(píng)論 5 349
  • 正文 年R本政府宣布睬隶,位于F島的核電站锣夹,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏苏潜。R本人自食惡果不足惜银萍,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,801評(píng)論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望窖贤。 院中可真熱鬧砖顷,春花似錦、人聲如沸赃梧。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,272評(píng)論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽授嘀。三九已至物咳,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間蹄皱,已是汗流浹背览闰。 一陣腳步聲響...
    開封第一講書人閱讀 33,393評(píng)論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留巷折,地道東北人压鉴。 一個(gè)月前我還...
    沈念sama閱讀 48,808評(píng)論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像锻拘,于是被迫代替她去往敵國和親油吭。 傳聞我的和親對象是個(gè)殘疾皇子击蹲,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,440評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容

  • Migrate不生成表 makemigrations 和 migrate ,新加的這兩個(gè)實(shí)體無法生成表婉宰。 1歌豺, 刪...
    7d4b0b51c9d4閱讀 894評(píng)論 0 0
  • 1. 版本 了解* 可以放在url,這種是比較推薦的心包。它需要配套的路由类咧,一般所有的CBV都要使用,所以放在全局配...
    上帝大人閱讀 166評(píng)論 0 1
  • 久違的晴天蟹腾,家長會(huì)痕惋。 家長大會(huì)開好到教室時(shí),離放學(xué)已經(jīng)沒多少時(shí)間了岭佳。班主任說已經(jīng)安排了三個(gè)家長分享經(jīng)驗(yàn)血巍。 放學(xué)鈴聲...
    飄雪兒5閱讀 7,524評(píng)論 16 22
  • 今天感恩節(jié)哎,感謝一直在我身邊的親朋好友珊随。感恩相遇述寡!感恩不離不棄。 中午開了第一次的黨會(huì)叶洞,身份的轉(zhuǎn)變要...
    迷月閃星情閱讀 10,567評(píng)論 0 11
  • 可愛進(jìn)取鲫凶,孤獨(dú)成精。努力飛翔衩辟,天堂翱翔螟炫。戰(zhàn)爭美好,孤獨(dú)進(jìn)取艺晴。膽大飛翔昼钻,成就輝煌。努力進(jìn)取封寞,遙望然评,和諧家園”肪浚可愛游走...
    趙原野閱讀 2,735評(píng)論 1 1