zoukankan      html  css  js  c++  java
  • jwt实战

    1 JWT梳理

    1.1 原理及其实现

    1.1.1 与传统不同之处
    1)基于传统的token验证

    用户登录,服务端给返回token,并将token保存在服务端 以后用户再来访问时,需要携带token,服务端获取token后,再去数据库中获取token进行校验。

    2)jwt

    用户登录,服务端给用户返回一个token(服务端不保存),以后用户再来访问,需要携带token,服务端获取token后,再做token校验。 优势:相较于传统的token相比,它无需在服务端保存token。

    1.1.2 jwt实现过程
    • 用户提交用户名和密码给服务器,使用jwt创建一个token,并给用户返回

    eyJ0eXAiOiJqd3QiLCJhbGciOiJIUzI1NiJ9.
    eyJ1c2VyX2lkIjoxLCJ1c2VybmFtZSI6Im1hcGVsIiwiZXhwIjoxNjA0MjE5MTk4fQ.
    VT32zm5t-LWItoRO5h4p2RFFV4bbOxneAqvQkK7yEno

    注意:jwt生成的token是由三段字符串组成,并且用 . 连接起来

    1)第一段
    HEADER,内部包含算法/token类型
    json转化成字符串后,做base64url(base64加密:+_.)
    {
        'alg': 'HS256',
        'typ': 'JWT'
    }
    2)第二段
    payload,自定义值
    json转化字符串,然后base64url加密(base64加密:+_.)
    {
        'id': '123123',
        'name': 'MP',
        'exp': 1516239022       # 超时时间
    }
    3)第三段
    第一步:第1,2部分密文拼接起来
    eyJ0eXAiOiJqd3QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxLCJ1c2VybmFtZSI6Im1hcGVsIiwiZXhwIjoxNjA0MjE5MTk4fQ
    第二步:对前2部分密文进行HS256加密 + 加盐
    第三步:对HS256加密后的密文再做base64url加密
    • 以后用户再来访问的时候,需要携带token,后端需要对token进行校验

      • 获取token

        • 第一步:对token进行切割

        eyJ0eXAiOiJqd3QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxLCJ1c2VybmFtZSI6Im1hcGVsIiwiZXhwIjoxNjA0MjE5MTk4fQ.VT32zm5t-LWItoRO5h4p2RFFV4bbOxneAqvQkK7yEno
        • 第二步:对第二段进行base64url解密,并获取payload信息,检测token是否已经超时

        {
            'id': '123123',
            'name': 'MP',
            'exp': 1516239022       # 超时时间
        }
        • 第三步:把第一、二段拼接,再次执行HS256加密

        第一步:第1,2部分密文拼接起来
        eyJ0eXAiOiJqd3QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxLCJ1c2VybmFtZSI6Im1hcGVsIiwiZXhwIjoxNjA0MjE5MTk4f

        第二步:对前2部分密文进行HS256加密 + 加盐
        密文 = base64解密(VT32zm5t-LWItoRO5h4p2RFFV4bbOxneAqvQkK7yEno)
        如果相等,表示token未被修改过(认证通过)
    1.1.3 应用
    pip install pyjwt

    1.2 jwt原理代码

    1.2.1 models.py
    from django.db import models
    ​
    class Person(models.Model):
        username = models.CharField(max_length=30)
        password = models.CharField(max_length=256)
        class Meta:
            db_table = 'person'
    1.2.2 views.py
    from rest_framework.response import Response
    from rest_framework.views import APIView
    from .models import Person
    import uuid
    salt = str(uuid.uuid4())
    # 验证时候需要的盐,不能改变
    class JWTLoginView(APIView):
        def post(self, request):
            username = request.data.get('username')
            password = request.data.get('password')
            person_obj = Person.objects.filter(username=username,password=password).first()
            if person_obj:
                import jwt
                # 导入jwt
                import datetime
                # 导入过期时间
                # 构造header
                headers = {
                    'typ': 'jwt',
                    'alg': 'HS256'
                }
                # 构造payload
                pyload = {
                    'user_id': person_obj.pk,
                    'username': username,
                    'exp': datetime.datetime.utcnow() + datetime.timedelta(minutes=5)           # 超时时间
                }
                token = jwt.encode(payload=pyload, key=salt, algorithm='HS256', headers=headers).decode('utf-8')
                '''
                'data': "eyJ0eXAiOiJqd3QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxLCJ1c2VybmFtZSI6Im1hcGVsIiwiZXhwIjoxNjA0MjE3NTEyfQ.vVm5PBVkFPscsmWc58eMYBR_pt9Gn2r7anN4pyNQc8Y"
                '''
                return Response(
                    {'code':1001, 'data': token}
                )
    ​
    class JWTOrderView(APIView):
        def get(self, request, *args, **kwargs):
            # 获取token并判断token的合法性
            token = request.query_params.get('token')
            # 1.切割
            # 2.解密第二段/判断过期
            # 3.验证第三段合法性
            import jwt
            from jwt import exceptions
            payload = None
            msg = None
            try:
                payload = jwt.decode(token, salt, True)
                # True集成时间校验和内部合法性校验
            except exceptions.ExpiredSignatureError:
                msg = 'token已经失效'
            except jwt.DecodeError:
                msg = 'token认证失败'
            except jwt.InvalidIssuer:
                msg = '非法的token'if not payload:
                return Response(
                    {'code':1003, 'error':msg}
                )
            print(payload['user_id'], payload['username'])
            return Response('验证通过,可以继续访问')
    1.2.3 urls.py
    # -*- coding: utf-8 -*-
    from django.urls import path
    from . import views
    urlpatterns = [
        path('login/', views.JWTLoginView.as_view()),   # 登录成功生成token
        path('jwt/', views.JWTOrderView.as_view()),     # 验证token
    ]

    2 JWT实战实现

    2.1 架构

    2.1.1 创建所需
    • 注意事项

    settings中的
    ​
    REST_FRAMEWORK = {
        'DEFAULT_AUTHENTICATION_CLASSES':[
            'app01.extensions.auth.JwtQueryAuthentication'
        #     默认所有页面都得登陆成功才能访问
        ]
    }
    'DEFAULT_AUTHENTICATION_CLASSES' 来源于 APIView 源码
    'app01.extensions.auth.JwtQueryAuthentication' 来源于auth.py 的路径

    2.2 代码实现

    2.2.1 settings.py
    # 末尾处添加
    REST_FRAMEWORK = {
        'DEFAULT_AUTHENTICATION_CLASSES':[
            'app01.extensions.auth.JwtQueryAuthentication'
        #     默认所有页面都得登陆成功才能访问
        ]
    }
    2.2.2 apps/app01/extensions/auth.py
    # -*- coding: utf-8 -*-
    from rest_framework.authentication import BaseAuthentication
    from rest_framework.exceptions import AuthenticationFailed
    from django.conf import settings
    class JwtQueryAuthentication(BaseAuthentication):
    ​
        def authenticate(self, request):
            # 获取token并判断token的合法性
            token = request.query_params.get('token')
            # 1.切割
            # 2.解密第二段/判断过期
            # 3.验证第三段合法性
            import jwt
            from jwt import exceptions
            salt = settings.SECRET_KEY
            payload = None
            msg = None
            try:
                payload = jwt.decode(token, salt, True)
                # True集成时间校验和内部合法性校验
            except exceptions.ExpiredSignatureError:
                raise AuthenticationFailed(
                    {'code': 1003, 'error':'token已经失效'}
                )
            except jwt.DecodeError:
                raise AuthenticationFailed(
                    {'code': 1003, 'error':'token认证失败'}
                )
            except jwt.InvalidIssuer:
                raise AuthenticationFailed(
                    {'code': 1003, 'error':'非法的token'}
                )
    ​
            return (payload,token)
    ​
    ​
    # 三种操作
    # 1.抛出异常,后续不再执行;
    # 2.return一个元祖(1,2),认证通过,在视图中如果调用request.user,就是元祖中的第一个值:request.auth,
    # 3.None
    2.2.3 apps/app01/utils/jwt_auth.py
    # -*- coding: utf-8 -*-
    import jwt
    # 导入jwt
    import datetime
    # 导入过期时间
    from django.conf import settings
    ​
    ​
    def create_token(payload, timeout=1):
        salt = settings.SECRET_KEY
        # 构造header
        headers = {
            'typ': 'jwt',
            'alg': 'HS256'
        }
        # 构造payload
        payload['exp'] = datetime.datetime.utcnow() + datetime.timedelta(minutes=5)  # 超时时间
    ​
        token = jwt.encode(payload=payload, key=salt, algorithm='HS256', headers=headers).decode('utf-8')
    ​
        return token
    2.2.4 views.py
    from rest_framework.response import Response
    from rest_framework.views import APIView
    from .models import Person
    from app01.extensions.auth import JwtQueryAuthentication
    from app01.utils.jwt_auth import create_token
    ​
    class ProLoginView(APIView):
        authentication_classes = []
        # 优先级更高(为了防止登录页面不停验证产生死循环)
        def post(self, request):
            username = request.data.get('username')
            password = request.data.get('password')
            person_obj = Person.objects.filter(username=username,password=password).first()
            if not person_obj:
                return Response(
                    {'code':1000, 'data': '用户名或者密码错误'}
                )
            token = create_token({'user_id': person_obj.id, 'name':person_obj.username})
            return Response(
                {'code': 1001, 'data':token}
            )
    ​
    ​
    class ProOrderView(APIView):
        # authentication_classes = [JwtQueryAuthentication] 不用添加
        def get(self, request, *args, **kwargs):
            print(request.data)
            return Response('订单列表')
    2.2.5 urls/py
    # -*- coding: utf-8 -*-
    from django.urls import path
    from . import views
    urlpatterns = [
        path('pro_login/', views.ProLoginView.as_view()),  # 登录成功生成token
        path('pro_jwt/', views.ProOrderView.as_view()),    # 验证token
    ]

     

    2.3 编码解码问题

    • Python encode() 方法以 encoding 指定的编码格式编码字符串。errors参数可以指定不同的错误处理方案。

    str.encode(encoding='UTF-8',errors='strict')
    • decode() 方法以 encoding 指定的编码格式解码字符串。默认编码为字符串编码

    str.decode(encoding='UTF-8',errors='strict')
    '''
    encoding -- 要使用的编码,如"UTF-8"。
    errors -- 设置不同错误的处理方案。默认为 'strict',意为编码错误引起一个UnicodeError。 其他可能得值有 'ignore', 'replace', 'xmlcharrefreplace', 'backslashreplace' 以及通过codecs.register_error() 注册的任何值。
    '''
    • son.dumps是将一个Python数据类型列表进行json格式的编码解析

    import json #导入python 中的json模块
    l = [‘iplaypython’,[1,2,3], {‘name’:’xiaoming’}] #创建一个l列表
    encoded_json = json.dumps(l) # 将l列表,进行json格式化编码
    print repr(l)
    print encoded_json #输出结果
    • json.dump和dumps差一个s,功能作用大致上是一样,也是将数据转换成str格式,最终包括了讲数据写入json文件的一个操作步骤,json.dump(data, file-open,ascii=False),可以包含三个属性,第三个ascii是用来避免出现unicode写入的乱码的;

    • json.load加载json格式文件

    • loads用来将字符型数据转换成原来的数据格式,诸如列表和字典,就是原本是什么格式就还原成什么格式。

    3 JWT实战实现(二)

    3.1 主要代码

    3.1.1 settings.py
    """
    Django settings for syl project.
    
    Generated by 'django-admin startproject' using Django 2.2.
    
    For more information on this file, see
    https://docs.djangoproject.com/en/2.2/topics/settings/
    
    For the full list of settings and their values, see
    https://docs.djangoproject.com/en/2.2/ref/settings/
    """
    import datetime
    import os
    
    # Build paths inside the project like this: os.path.join(BASE_DIR, ...)
    import sys
    
    BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    sys.path.insert(0, os.path.join(BASE_DIR, 'apps'))
    
    # Quick-start development settings - unsuitable for production
    # See https://docs.djangoproject.com/en/2.2/howto/deployment/checklist/
    
    # SECURITY WARNING: keep the secret key used in production secret!
    SECRET_KEY = '8s8wp%i7i_cq)+f5nfx!%q&*$4)0pq-kv*)!7#n6&6v)8b3h^p'
    
    # SECURITY WARNING: don't run with debug turned on in production!
    DEBUG = True
    
    ALLOWED_HOSTS = ['*']
    
    
    # Application definition
    
    INSTALLED_APPS = [
        'django.contrib.admin',
        'django.contrib.auth',
        'django.contrib.contenttypes',
        'django.contrib.sessions',
        'django.contrib.messages',
        'django.contrib.staticfiles',
        'rest_framework',
        'apps.user.apps.UserConfig',
        'corsheaders',
        'django_filters',
        # 过滤器
        # 'rest_framework_jwt',
        # 'rest_framework.authentication'
    ]
    
    MIDDLEWARE = [
        'corsheaders.middleware.CorsMiddleware',
        'django.middleware.security.SecurityMiddleware',
        'django.contrib.sessions.middleware.SessionMiddleware',
        'django.middleware.common.CommonMiddleware',
        # 'django.middleware.csrf.CsrfViewMiddleware',
        'django.contrib.auth.middleware.AuthenticationMiddleware',
        'django.contrib.messages.middleware.MessageMiddleware',
        'django.middleware.clickjacking.XFrameOptionsMiddleware',
    ]
    
    ROOT_URLCONF = 'syl.urls'
    
    TEMPLATES = [
        {
            'BACKEND': 'django.template.backends.django.DjangoTemplates',
            'DIRS': [],
            'APP_DIRS': True,
            'OPTIONS': {
                'context_processors': [
                    'django.template.context_processors.debug',
                    'django.template.context_processors.request',
                    'django.contrib.auth.context_processors.auth',
                    'django.contrib.messages.context_processors.messages',
                ],
            },
        },
    ]
    
    WSGI_APPLICATION = 'syl.wsgi.application'
    
    
    # Database
    # https://docs.djangoproject.com/en/2.2/ref/settings/#databases
    
    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.mysql',
            'NAME': 'syldb',
            'USER': 'root',
            'PASSWORD': '1',
            'HOST': '127.0.0.1',
            'PORT': 3306
        }
    }
    CORS_ALLOW_WHITELIST = (
        'http://localhost:8080',
        'http://127.0.0.1:8888'
    )
    CORS_ALLOW_CREDENTIALS = True
    
    REST_FRAMEWORK = {
        # 文档报错: AttributeError: ‘AutoSchema’ object has no attribute ‘get_link’
        # 用下面的设置可以解决
        'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.AutoSchema',
        # 默认设置是:
        # 'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.openapi.AutoSchema',
    
        # 异常处理器
        # 'EXCEPTION_HANDLER': 'user.utils.exception_handler',
    
        # Base API policies      默认渲染器类
        'DEFAULT_RENDERER_CLASSES': [
            'rest_framework.renderers.JSONRenderer',
            'rest_framework.renderers.BrowsableAPIRenderer',
        ],
        # 默认解析器类
        'DEFAULT_PARSER_CLASSES': [
            'rest_framework.parsers.JSONParser',
            'rest_framework.parsers.FormParser',
            'rest_framework.parsers.MultiPartParser'
        ],
        # 1.认证器(全局)
        'DEFAULT_AUTHENTICATION_CLASSES': [
            'rest_framework_jwt.authentication.JSONWebTokenAuthentication',  # 在 DRF中配置JWT认证
            # 'rest_framework.authentication.SessionAuthentication',  # 使用session时的认证器
            # 'rest_framework.authentication.BasicAuthentication'  # 提交表单时的认证器
        ],
    
        # 2.权限配置(全局): 顺序靠上的严格
        'DEFAULT_PERMISSION_CLASSES': [
            # 'rest_framework.permissions.IsAdminUser',  # 管理员可以访问
            'rest_framework.permissions.IsAuthenticated',  # 认证用户可以访问
            # 'rest_framework.permissions.IsAuthenticatedOrReadOnly',  # 认证用户可以访问, 否则只能读取
            # 'rest_framework.permissions.AllowAny',  # 所有用户都可以访问
        ],
        # 3.限流(防爬虫)
        'DEFAULT_THROTTLE_CLASSES': [
            'rest_framework.throttling.AnonRateThrottle',
            'rest_framework.throttling.UserRateThrottle',
        ],
        # 3.1限流策略
        # 'DEFAULT_THROTTLE_RATES': {
        #     'user': '100/hour',  # 认证用户每小时100次
        #     'anon': '300/day',  # 未认证用户每天能访问3次
        # },
    
        'DEFAULT_CONTENT_NEGOTIATION_CLASS': 'rest_framework.negotiation.DefaultContentNegotiation',
        'DEFAULT_METADATA_CLASS': 'rest_framework.metadata.SimpleMetadata',
        'DEFAULT_VERSIONING_CLASS': None,
    
        # 4.分页(全局):全局分页器, 例如 省市区的数据自定义分页器, 不需要分页
        # 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
        # 每页返回数量
        # 'PAGE_SIZE': 1
        # 5.过滤器后端
        'DEFAULT_FILTER_BACKENDS': [
            'django_filters.rest_framework.DjangoFilterBackend',
            # 'django_filters.rest_framework.backends.DjangoFilterBackend', 包路径有变化
        ],
    
        # 5.1过滤排序(全局):Filtering 过滤排序
        'SEARCH_PARAM': 'search',
        'ORDERING_PARAM': 'ordering',
    
        'NUM_PROXIES': None,
    
        # 6.版本控制:Versioning  接口版本控制
        'DEFAULT_VERSION': None,
        'ALLOWED_VERSIONS': None,
        'VERSION_PARAM': 'version',
    
        # Authentication  认证
        # 未认证用户使用的用户类型
        'UNAUTHENTICATED_USER': 'django.contrib.auth.models.AnonymousUser',
        # 未认证用户使用的Token值
        'UNAUTHENTICATED_TOKEN': None,
    
        # View configuration
        'VIEW_NAME_FUNCTION': 'rest_framework.views.get_view_name',
        'VIEW_DESCRIPTION_FUNCTION': 'rest_framework.views.get_view_description',
    
        'NON_FIELD_ERRORS_KEY': 'non_field_errors',
    
        # Testing
        'TEST_REQUEST_RENDERER_CLASSES': [
            'rest_framework.renderers.MultiPartRenderer',
            'rest_framework.renderers.JSONRenderer'
        ],
        'TEST_REQUEST_DEFAULT_FORMAT': 'multipart',
    
        # Hyperlink settings
        'URL_FORMAT_OVERRIDE': 'format',
        'FORMAT_SUFFIX_KWARG': 'format',
        'URL_FIELD_NAME': 'url',
    
        # Encoding
        'UNICODE_JSON': True,
        'COMPACT_JSON': True,
        'STRICT_JSON': True,
        'COERCE_DECIMAL_TO_STRING': True,
        'UPLOADED_FILES_USE_URL': True,
    
        # Browseable API
        'HTML_SELECT_CUTOFF': 1000,
        'HTML_SELECT_CUTOFF_TEXT': "More than {count} items...",
    
        # Schemas
        'SCHEMA_COERCE_PATH_PK': True,
        'SCHEMA_COERCE_METHOD_NAMES': {
            'retrieve': 'read',
            'destroy': 'delete'
        },
    
        # 'Access-Control-Allow-Origin':'http://localhost:8080',
        # 'Access-Control-Allow-Credentials': True
    }
    # Password validation
    # https://docs.djangoproject.com/en/2.2/ref/settings/#auth-password-validators
    
    AUTH_PASSWORD_VALIDATORS = [
        {
            'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
        },
        {
            'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
        },
        {
            'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
        },
        {
            'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
        },
    ]
    
    
    # Internationalization
    # https://docs.djangoproject.com/en/2.2/topics/i18n/
    
    LANGUAGE_CODE = 'zh-hans'
    
    TIME_ZONE = 'Asia/Shanghai'
    
    USE_I18N = True
    
    USE_L10N = True
    
    USE_TZ = False
    
    
    # Static files (CSS, JavaScript, Images)
    # https://docs.djangoproject.com/en/2.2/howto/static-files/
    
    STATIC_URL = '/static/'
    AUTH_USER_MODEL = 'user.User'
    
    # jwt载荷中的有效期设置
    JWT_AUTH = {
        # 1.token前缀:headers中 Authorization 值的前缀
        'JWT_AUTH_HEADER_PREFIX': 'JWT',
        # 2.token有效期:一天有效
        'JWT_EXPIRATION_DELTA': datetime.timedelta(days=1),
        # 3.刷新token:允许使用旧的token换新token
        'JWT_ALLOW_REFRESH': True,
        # 4.token有效期:token在24小时内过期, 可续期token
        'JWT_REFRESH_EXPIRATION_DELTA': datetime.timedelta(hours=24),
        # 5.自定义JWT载荷信息:自定义返回格式,需要手工创建
        'JWT_RESPONSE_PAYLOAD_HANDLER': 'user.utils.jwt_response_payload_handler',
    }
    
    # AUTHENTICATION_BACKENDS = ['user.utils.EmailAuthBackend']
    3.1.2 views.py
    import datetime
    import random
    
    from django.http import HttpResponse
    from django_filters.rest_framework import DjangoFilterBackend
    from rest_framework import viewsets
    from rest_framework.authentication import BasicAuthentication,SessionAuthentication
    from rest_framework.decorators import action
    from rest_framework.filters import OrderingFilter
    from rest_framework.permissions import AllowAny, IsAdminUser, IsAuthenticated,IsAuthenticatedOrReadOnly
    from rest_framework.response import Response
    from rest_framework.throttling import UserRateThrottle
    from rest_framework.pagination import PageNumberPagination
    from rest_framework.views import APIView
    from rest_framework.permissions import BasePermission, SAFE_METHODS
    from user.models import User
    from user.serializers import UserSerializer, UserUnActiveSerializer
    
    def index(request):
        # 需要认证才能访问的视图
        return HttpResponse('hello')
    
    # 分页(局部):自定义分页器 局部
    class PageNum(PageNumberPagination):
        # 查询字符串中代表每页返回数据数量的参数名, 默认值: None
        page_size_query_param = 'page_size'
        # 查询字符串中代表页码的参数名, 有默认值: page
        # page_query_param = 'page'
        # 一页中最多的结果条数
        max_page_size = 2
    
    # 自定义权限(局部)
    class MyPermission(BasePermission):
        # has_permission 是用户对这个视图有没有 GET POST PUT PATCH DELETE 权限的分别判断
        def has_permission(self, request, view):
            print('has_perm')
        # print(view.kwargs.get("pk"), request.user.id)
    
        # """判断用户对模型有没有访问权"""
        # 任何用户对使用此权限类的视图都有访问权限
            if request.user.is_superuser:
        # 管理员对用户模型有访问权
                return True
            elif view.kwargs.get('pk') == str(request.user.id):
        # 携带的id和用户的id相同时有访问权
                return True
            return False
    
        # has_object_permission 是用户过了 has_permission 判断有权限以后,再判断这个用户有没有对一个具体的对象有没有操作权限
        # 这样设置以后,即使是django admin管理员也只能查询自己user标的信息,不能查询其他用户的单条信息
    
        def has_object_permission(self, request, view, obj):
            print('has_object_perm')
            """获取单个数据时,判断用户对某个数据对象是否有访问权限"""
            if request.user.id == obj.id:
                return True
            return False
    
    class UserViewSet(viewsets.ModelViewSet):
    
        """
        完成产品的增删改查
        """
        queryset = User.objects.all()
        serializer_class = UserSerializer
        # 优先使用 get_serializer_class 返回的序列化器
    
        # 自定义权限会覆盖掉全局权限!!!
        # # 1.认证:自定义认证类, 自定义会覆盖全局配置
        # authentication_classes = (BasicAuthentication, SessionAuthentication)
        # # 2.权限:自定义权限类
        # permission_classes = (MyPermission,)
        # 3.分页:自定义分页器 覆盖全局配置
        pagination_class = PageNum
        # 4.限流:自定义限流类
        throttle_classes = [UserRateThrottle]
        # 5.过滤:指定过滤方法类, 排序方法类, 一个或多个
        filter_backends = (DjangoFilterBackend, OrderingFilter) # 同时支持过滤和排序
        # 5.1指定排序字段, 不设置, 排序功能不起效
        ordering_fields = ('date_joined', 'id') # ?ordering=-id
        # 5.2指定过滤字段, 不设置, 过滤功能不起效
        filter_fields = ('username', 'phone', 'is_active') # ?username=tom&phone=&is_active=true
    
    
        # 根据不同的请求, 获得不同的序列化器
        def get_serializer_class(self):
            if self.action == 'unactived':
                return UserUnActiveSerializer
            else:
                return UserSerializer
        @action(methods=['get'], detail=False)
        def unactived(self, request, *args, **kwargs):
            # 获取查询集, 过滤出未激活的用户
            qs = self.queryset.filter(is_active=False)
            # 使用序列化器, 序列化查询集, 并且是
            ser = self.get_serializer(qs, many=True)
            return Response(ser.data)
    
        @action(methods=['get'], detail=False)
        def actived(self, request, *args, **kwargs):
            # 获取查询集, 过滤出未激活的用户
            qs = self.queryset.filter(is_active=True)
            # 使用序列化器, 序列化查询集, 并且是
            ser = self.get_serializer(qs, many=True)
            return Response(ser.data)
    
    class RegisterView(APIView):
        """
            用户注册, 权限是: 匿名用户可访问
        """
        # 自定义权限类
        permission_classes = (AllowAny,)
    
        def post(self, request):
            """
                接收邮箱和密码, 前端校验两遍一致性, 注册成功后返回成功, 然后用户自行登录获取token
                1. 随机用户名
                2. 生成用户
                3. 设置用户密码
                4. 保存用户
                :param request:
                :return: {'code':0,'msg':'注册成功'}
            """
            email = request.data.get('email')
            passwrod = request.data.get('password')
            if all([email, passwrod]):
                pass
            else:
                return Response({'code':9999,'msg':'参数不全'})
    
            rand_name = self.randomUsername()
    
            user = User(username=rand_name, email=email)
            user.set_password(passwrod)
            user.save()
    
            return Response({'code': 0, 'msg': '注册成功'})
        def randomUsername(self):
            """
                生成随机用户名: 格式: SYL + 年月日时分 + 5位随机数
                :return:
                """
            d = datetime.datetime.now()
            base = 'SYL'
            time_str = '%04d%02d%02d%02d%02d' % (d.year, d.month, d.day, d.hour, d.minute)
            rand_num = str(random.randint(10000, 99999))
            return base + time_str + rand_num
    3.1.3 主路由urls.py
    """syl URL Configuration
    
    The `urlpatterns` list routes URLs to views. For more information please see:
        https://docs.djangoproject.com/en/2.2/topics/http/urls/
    Examples:
    Function views
        1. Add an import:  from my_app import views
        2. Add a URL to urlpatterns:  path('', views.home, name='home')
    Class-based views
        1. Add an import:  from other_app.views import Home
        2. Add a URL to urlpatterns:  path('', Home.as_view(), name='home')
    Including another URLconf
        1. Import the include() function: from django.urls import include, path
        2. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
    """
    from django.contrib import admin
    from django.urls import path, include
    
    urlpatterns = [
        path('admin/', admin.site.urls),
        path('user/', include('apps.user.urls'))
    ]
    3.1.4 子路由urls.py
    # -*- coding: utf-8 -*-
    from django.urls import include, path
    from rest_framework.authtoken.views import obtain_auth_token
    from user import views
    from rest_framework.routers import SimpleRouter, DefaultRouter
    from rest_framework_jwt.views import obtain_jwt_token, refresh_jwt_token
    # 自动生成路由方法, 必须使用视图集
    # router = SimpleRouter() # 没有根路由 /user/ 无法识别
    router = DefaultRouter() # 有根路由
    router.register(r'user', views.UserViewSet)
    urlpatterns = [
        path('index/', views.index),    # 函数视图
        path('login/', obtain_jwt_token), # 获取token,登录视图
        path('refresh/', refresh_jwt_token), # 刷新token
        path('api-auth/', include('rest_framework.urls',namespace='rest_framework')), # 认证地址
        path('register/', views.RegisterView.as_view()), # 注册视图, /user/register/
    ]
    urlpatterns += router.urls # 模块地址
    # print(router.urls)
    3.1.5 models.py
    from django.contrib.auth.models import AbstractUser
    from django.db import models
    
    # Create your models here.
    
    class User(AbstractUser):
        phone = models.CharField('手机号', max_length=20)
        img = models.ImageField(upload_to='user', null=True)
        nick_name = models.CharField('昵称', max_length=20)
        address = models.CharField('地址', max_length=255)
        class Meta:
            db_table = 'tb_user'
    3.1.6 utils.py
    # -*- coding: utf-8 -*-
    
    def jwt_response_payload_handler(token, user=None, request=None, role=None):
        """
            自定义jwt认证成功返回数据
            :token 返回的jwt
            :user 当前登录的用户信息[对象]
            :request 当前本次客户端提交过来的数据
            :role 角色
        """
        if user.first_name:
            name = user.first_name
        else:
            name = user.username
            return {
                'authenticated': 'true',
                 'id': user.id,
                 "role": role,
                 'name': name,
                 'username': user.username,
                 'email': user.email,
                 'token': token,
            }
    
    # 以前使用username进行用户验证,现在修改成email进行验证
    
    class EmailAuthBackend:
        def authenticate(self, request, username=None, password=None):
            try:
                user = User.objects.get(username=username)
            except Exception as e:
                user = None
            if not user:
                try:
                    user = User.objects.get(email=username)
                except Exception as e:
                    user = None
            if user and user.check_password(password):
                return user
            else:
                return None
        def get_user(self, user_id):
            try:
                return User.objects.get(pk=user_id)
            except User.DoesNotExist:
                return None
  • 相关阅读:
    Maven入门:使用Nexus搭建Maven私服及上传下载jar包
    idea 破解转(肉测好用,测试2018.4.16)
    使用spring-boot-admin对spring-boot服务进行监控(转自牛逼的人物)
    eureka集群高可用配置,亲测成功配置(转)
    Apollo-open-capacity-platform 微服务能力开发平台 (转)
    导入数据到数据库表,报错[Err] [Row1] [Imp] 1153
    .mmap文件如何打开
    web端自动化——webdriver驱动
    web端自动化——Remote应用
    svn服务器端—管理员分配权限
  • 原文地址:https://www.cnblogs.com/mapel1594184/p/13916788.html
Copyright © 2011-2022 走看看