day89

1 jwt认证介绍
2 base64介绍和使用
3 jwt基本使用(jwt内置,控制用户登录后能访问和未登陆能访问)
4 控制登录接口返回的数据格式
5 自定义基于jwt的认证类

## 4 jwt认证介绍

```python
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,就可以继续往后访问
```



## 5 base64介绍和使用

```python
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)
```



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

```python
1 drf中使用jwt,借助第三方https://github.com/jpadilla/django-rest-framework-jwt
2 pip3 install djangorestframework-jwt
3 快速使用(默认使用auth的user表)
    1 在默认auth的user表中创建一个用户
    2 在路由中配置
        path('login/', obtain_jwt_token),
    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.username),如果没有携带,当前登录用户就是匿名用户(request.user.username=None)

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


```



## 7 控制登录接口返回的数据格式

```python
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_RESPONSE_PAYLOAD_HANDLER': 'homework.utils.jwt_response_payload_handler',
    }
```



## 8 自定义基于jwt的认证类

```python
1 自己实现基于jwt的认证类,通过认证,才能继续访问,通不过认证就返回错误
2 代码如下
    
    class JwtAuthentication(BaseJSONWebTokenAuthentication):
        def authenticate(self, request):
            # 认证逻辑()
            # token信息可以放在请求头中,请求地址中
            # key值可以随意叫
            # token=request.GET.get('token')
            token=request.META.get('HTTP_Authorization'.upper())
            # 校验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, ]
```

settings.py
import datetime

JWT_AUTH = {
    # 过期时间
    'JWT_EXPIRATION_DELTA': datetime.timedelta(days=7),
    # 自定义认证结果:见下方序列化user和自定义response
    # 如果不自定义,返回的格式是固定的,只有token字段
    'JWT_RESPONSE_PAYLOAD_HANDLER': 'app01.utils.jwt_response_payload_handler',
}

urls.py
from django.contrib import admin
from django.urls import path
from app01 import views
from rest_framework_jwt.views import obtain_jwt_token
urlpatterns = [
    path('admin/', admin.site.urls),
    path('login/', obtain_jwt_token),
    path('order/', views.OrderView.as_view()),
]

serializer.py
from rest_framework import serializers
from app01.models import UserInfo
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, required=True, write_only=True)

    class Meta:
        model = 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:
            # re_password不存数据库,剔除
            attrs.pop('re_password')
            return attrs
        else:
            raise ValidationError('两次密码不一致')

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

    def create(self, validated_data):
        # re_password也可也在这里移除
        # UserInfo.objects.create(**validated_data) # 密码是明文
        user = UserInfo.objects.create_user(**validated_data)
        return user  # 不要忘了这句话


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


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

utils.py
from rest_framework.response import Response
from app01.serializer import UserReadOnlyModelSerializer


def jwt_response_payload_handler(token, user=None, request=None):  # 参数token,user,request
    return {'code': 100, 'msg': '成功', 'token': token, 'user': UserReadOnlyModelSerializer(instance=user).data}

my_jwtauth.py
# 自定义认证类
import jwt
from rest_framework_jwt.utils import jwt_decode_handler
from rest_framework_jwt.authentication import JSONWebTokenAuthentication, BaseJSONWebTokenAuthentication
from rest_framework import exceptions
from rest_framework.authentication import BaseAuthentication


class MyJwtAuthentication(BaseJSONWebTokenAuthentication):
    def authenticate(self, request):
        token = request.META.get('HTTP_AUTHORIZATION')
        try:
            payload = jwt_decode_handler(token)
        except jwt.ExpiredSignature:
            msg = 'Signature has expired.'
            raise exceptions.AuthenticationFailed(msg)
        except jwt.DecodeError:
            msg = 'Error decoding signature.'
            raise exceptions.AuthenticationFailed(msg)
        except jwt.InvalidTokenError:
            raise exceptions.AuthenticationFailed('未知错误')

        user = self.authenticate_credentials(payload)

        return (user, token)

views.py
from django.shortcuts import render

# Create your views here.
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


# class OrderView(APIView):
#     authentication_classes = [JSONWebTokenAuthentication, ]
#     permission_classes = [IsAuthenticated, ]
#
#     def get(self, response):
#         return Response('订单')


from app01 import my_jwtauth
class OrderView(APIView):
    authentication_classes = [my_jwtauth.MyJwtAuthentication, ]    # 使用自定义认证类

    def get(self, response):
        return Response('订单')

 

posted @ 2020-11-15 18:54  板鸭没有腿  阅读(122)  评论(0)    收藏  举报