zoukankan      html  css  js  c++  java
  • django djangorestframework 常规操作

     什么是JWT?

    Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519).该token被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景。JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密。

    开发环境:

    python 运行环境3.8

    django 3.0

    开发工具 pycharm

    1.新建django项目如下

    项目名称 djangoJWT

    2.项目结构如下

    3.安装 JWT

    依次安装django-admin 美化库 django-simpleui  导入导出 django-import-export

    pip install djangorestframework

    pip install djangorestframework-jwt

    pip install django-simpleui
    pip install django-import-export

     4.JWT配置  

    settings.py 配置如下

    """
    Django settings for djangoJWT project.
    
    Generated by 'django-admin startproject' using Django 3.1.
    
    For more information on this file, see
    https://docs.djangoproject.com/en/3.1/topics/settings/
    
    For the full list of settings and their values, see
    https://docs.djangoproject.com/en/3.1/ref/settings/
    """
    import os, datetime
    from pathlib import Path
    
    # Build paths inside the project like this: BASE_DIR / 'subdir'.
    BASE_DIR = Path(__file__).resolve(strict=True).parent.parent
    
    # Quick-start development settings - unsuitable for production
    # See https://docs.djangoproject.com/en/3.1/howto/deployment/checklist/
    
    # SECURITY WARNING: keep the secret key used in production secret!
    SECRET_KEY = '2p8r+(aw&@0a3mgk&i62qbqzrp@*3n*zjb48q-36k4+0i2fqrz'
    
    # SECURITY WARNING: don't run with debug turned on in production!
    DEBUG = True
    
    ALLOWED_HOSTS = ["*"]
    
    # Application definition
    
    INSTALLED_APPS = [
        'simpleui',
        'import_export',
        'django.contrib.admin',
        'django.contrib.auth',
        'django.contrib.contenttypes',
        'django.contrib.sessions',
        'django.contrib.messages',
        'django.contrib.staticfiles',
        'app.apps.AppConfig',
        'rest_framework',
        'rest_framework.authtoken',
    ]
    
    MIDDLEWARE = [
        '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 = 'djangoJWT.urls'
    
    TEMPLATES = [
        {
            'BACKEND': 'django.template.backends.django.DjangoTemplates',
            'DIRS': [os.path.join(BASE_DIR, 'templates')]
            ,
            '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 = 'djangoJWT.wsgi.application'
    
    # Database
    # https://docs.djangoproject.com/en/3.1/ref/settings/#databases
    
    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.sqlite3',
            'NAME': BASE_DIR / 'db.sqlite3',
        }
    }
    
    # Password validation
    # https://docs.djangoproject.com/en/3.1/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',
        },
    ]
    
    # REST_FRAMEWORK JWT 验证
    REST_FRAMEWORK = {
        # 设置所有接口都需要被验证
        'DEFAULT_PERMISSION_CLASSES': (
            # 'rest_framework.permissions.IsAuthenticated',  建议是特定接口特定认证
        ),
        # 用户登陆认证方式
        'DEFAULT_AUTHENTICATION_CLASSES': (
            'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
            'rest_framework.authentication.SessionAuthentication',
            'rest_framework.authentication.BasicAuthentication',
            'app.authentication.UserAuthentication',  # 用自定义的认证类
        ),
        'DEFAULT_PARSER_CLASSES': ['rest_framework.parsers.JSONParser', 'rest_framework.parsers.FormParser'],
    }
    # "UNAUTHENTICATED_USER": lambda : "匿名用户"
    DEFAULT_THROTTLE_RATES: {
        'user': '10/min',  # 登录的用户一分钟可以访问10次
        'anon': '3/min',  # 游客一分钟可以访问3次
    }
    
    JWT_AUTH = {
        'JWT_ENCODE_HANDLER':
            'rest_framework_jwt.utils.jwt_encode_handler',
    
        'JWT_DECODE_HANDLER':
            'rest_framework_jwt.utils.jwt_decode_handler',
    
        'JWT_PAYLOAD_HANDLER':
            'rest_framework_jwt.utils.jwt_payload_handler',
    
        'JWT_PAYLOAD_GET_USER_ID_HANDLER':
            'rest_framework_jwt.utils.jwt_get_user_id_from_payload_handler',
    
        'JWT_RESPONSE_PAYLOAD_HANDLER':
            'rest_framework_jwt.utils.jwt_response_payload_handler',
    
        # 这是用于签署JWT的密钥,确保这是安全的,不共享不公开的
        'JWT_SECRET_KEY': SECRET_KEY,
        'JWT_GET_USER_SECRET_KEY': None,
        'JWT_PUBLIC_KEY': None,
        'JWT_PRIVATE_KEY': None,
        'JWT_ALGORITHM': 'HS256',
        # 如果秘钥是错误的,它会引发一个jwt.DecodeError
        'JWT_VERIFY': True,
        'JWT_VERIFY_EXPIRATION': True,
        'JWT_LEEWAY': 0,
        # Token过期时间设置
        'JWT_EXPIRATION_DELTA': datetime.timedelta(minutes=5),
        'JWT_AUDIENCE': None,
        'JWT_ISSUER': None,
        # 是否开启允许Token刷新服务,及限制Token刷新间隔时间,从原始Token获取开始计算
        'JWT_ALLOW_REFRESH': False,
        'JWT_REFRESH_EXPIRATION_DELTA': datetime.timedelta(days=7),
        # 定义与令牌一起发送的Authorization标头值前缀
        'JWT_AUTH_HEADER_PREFIX': 'JWT',
        'JWT_AUTH_COOKIE': None,
    }
    # Internationalization
    # https://docs.djangoproject.com/en/3.1/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/3.1/howto/static-files/
    
    STATIC_URL = '/static/'

    5.自定义授权认证类 在app下新建--> authentication.py

    from rest_framework import exceptions
    from rest_framework.authentication import BaseAuthentication
    from rest_framework_jwt.settings import api_settings
    
    
    class UserAuthentication(BaseAuthentication):
        def authenticate(self, request):
            print("request.data==================", request.data)
            if 'token' in request.data:
                try:
                    token = request.data['token']
                    jwt_decode_handler = api_settings.JWT_DECODE_HANDLER
                    user_dict = jwt_decode_handler(token)
                    return (user_dict, token)
                except Exception as ex:
                    raise exceptions.AuthenticationFailed(detail={'code': 401, 'msg': 'token已过期'})
            else:
                raise exceptions.AuthenticationFailed(detail={'code': 400, 'msg': '缺少token'})
    
        def authenticate_header(self, request):
            pass

    6.app--> models.py 代码如下

    from django.db import models
    import datetime
    import uuid
    from django.contrib.auth.models import User
    # Create your models here.
    from django.utils.html import format_html
    from django.db.models import IntegerField, Model
    from django.core.validators import MaxValueValidator, MinValueValidator
    import datetime
    import random, os
    from django.contrib.auth.models import AbstractUser
    from django.db import models
    import djangoJWT.settings as config
    STATE_CHOICES = ((0, ""), (1, ""))
    AUTHTYPE_CHOICES = ((1, '消息'), (2, 'EOA'), (3, '组织架构'))
    DEVELOPMENTLANGUAGE_CHOICES = ((0, 'Python'), (1, 'Java'), (2, 'C#'), (3, 'GO'), (4, 'PHP'))
    # 方法重命名
    def rename(newname):
        def decorator(fn):
            fn.__name__ = newname
            return fn
        return decorator
    def newImageName(instance, filename):
        filename = '{}.{}'.format(uuid.uuid4().hex, "png")
        return filename
    # 生成预约订单号
    # 用时间生成一个唯一随机数
    
    def random_with_N_digits(n):
        range_start = 10 ** (n - 1)
        range_end = (10 ** n) - 1
        return random.randint(range_start, range_end)
    
    
    def get_ran_dom():
        nowTime = datetime.datetime.now().strftime("%Y%m%d%H%M%S")  # 生成当前时间
        randomNum = random_with_N_digits(3)  # 生成的随机整数n,其中0<=n<=100
        if randomNum <= 10:
            randomNum = str(0) + str(randomNum)
        uniqueNum = str(nowTime) + str(randomNum)
        return uniqueNum
    
    
    # Create your models here.
    # 实例化加解密对象
    
    # 应用管理
    class appManager(models.Model):
        name = models.CharField(verbose_name="应用名称", max_length=225, null=False, blank=False, default="")
        appid = models.CharField(verbose_name="应用Id", max_length=225, null=False, blank=False, default="")
        secret = models.CharField(verbose_name="应用秘钥", max_length=900, null=False, blank=False, default=None)
        authType = models.IntegerField(choices=AUTHTYPE_CHOICES, verbose_name="授权类型", null=False, blank=False, default=0)
        developmentLanguage = models.IntegerField(choices=DEVELOPMENTLANGUAGE_CHOICES, verbose_name="开发语言", null=False,
                                                  blank=False, default=0)
        state = models.IntegerField(choices=STATE_CHOICES, verbose_name="是否禁用", null=False, blank=False, default=1)
        contactPerson = models.TextField(verbose_name="联系人", max_length=500, null=False, blank=False, default="")
        whitelist = models.TextField(verbose_name="IP白名单", max_length=500, null=False, blank=False, default="")
        domain = models.TextField(verbose_name="请求域名", max_length=500, null=False, blank=False, default="")
    
        createTime = models.DateTimeField(auto_now_add=True, verbose_name="创建时间")
        lastTime = models.DateTimeField(auto_now=True, verbose_name="修改时间")
        creator = models.ForeignKey(User, null=True, blank=True, on_delete=models.SET_NULL, verbose_name="创建者",
                                    related_name="appmanager_creator")
        editor = models.ForeignKey(User, null=True, blank=True, on_delete=models.SET_NULL, verbose_name="修改者",
                                   related_name="appmanager_editor")
    
        class Meta:
            verbose_name = "应用管理"
            verbose_name_plural = "应用管理"
    
        def __str__(self):
            return self.name

    7.app-->admin.py 代码如下

    from django.contrib import admin
    from app import models
    import datetime
    import tablib
    from django.contrib import admin
    from django.db.models import QuerySet
    from django.utils.html import format_html
    from import_export import resources
    from import_export.admin import ImportExportModelAdmin
    from django.apps import apps
    from django import forms
    import uuid
    from django.contrib.auth.models import Permission, User
    
    admin.site.site_header = "XX管理"
    admin.site.site_title = "XX管理后台管理"
    
    
    # Register your models here.
    
    @admin.register(models.appManager)
    class appManagerAdmin(ImportExportModelAdmin):
        fields = (
            "name", "developmentLanguage", "state",
            "contactPerson", "whitelist", "domain")
        list_display = (
            "name", "appid", "secret", "developmentLanguage", "state",
            "createTime",
            "lastTime",
            "creator", "editor")
        list_display_links = ("name",)
        exclude = ("createTime", "creator", "editor")
        search_fields = ("name",)
        list_filter = ("developmentLanguage", "state")
        model_icon = "fa fa-tag"
        list_per_page = 20
        ordering = ["-id"]
    
        def save_model(self, request, obj, form, change):
            if form.is_valid():
                if change:
                    obj.editor = request.user
                else:
                    appid = str(uuid.uuid4())
                    secret = str(uuid.uuid4())
                    obj.appid = appid
                    obj.secret = secret
                    obj.creator = request.user
                    obj.editor = request.user
                    obj.save()
                    # 新创建一个授权用户
                    User.objects.create_user(username=appid, password=secret, is_staff=True, is_active=True,
                                             first_name=obj.name)
            super().save_model(request, obj, form, change)

    8.app--> views.py 代码如下

     

    from app import models
    from rest_framework_jwt.settings import api_settings
    from rest_framework.response import Response
    from rest_framework.views import APIView
    
    
    # Create your views here.
    
    class APIResponse(Response):
        def __init__(self, data_status=0, data_msg='ok', results=None
                     , http_status=None, headers=None, exception=False, **kwargs):
            # data的初始状态:状态码与状态信息
            data = {
                'stauts': data_status,
                'msg': data_msg,
            }
            # data的响应数据体
            # results可能是False、0等数据,这些数据某些情况下也会作为合法数据返回
            if results is not None:
                data['results'] = results
            # data响应的其他内容
            # if kwargs is not None:
            #     for k, v in kwargs:
            #         setattr(data, k, v)
            data.update(kwargs)
    
            # 重写父类的Response的__init__方法
            super().__init__(data=data, status=http_status, headers=headers, exception=exception)
    
    
    class LoginJWTAPIView(APIView):
        authentication_classes = ()
        permission_classes = ()
    
        def get(self, request, *args, **kwargs):
            return APIResponse(1, '密码错误')
    
        def post(self, request, *args, **kwargs):
            # username可能携带的不止是用户名,可能还是用户的其它唯一标识 手机号 邮箱
            print(request.data)
            appid = request.data.get('appid', None)
            secret = request.data.get('secret', None)
            if appid is None or secret is None:
                return APIResponse(-1, 'appid或secret不能为空!')
            user = models.User.objects.filter(username=appid).first()
            if user is None:
                return APIResponse(-2, 'appid或secret输入有误')
            # 获得用户后,校验密码并签发token
            if not user.check_password(secret):
                return APIResponse(-3, '密码错误')
            jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
            jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
            payload = jwt_payload_handler(user)
            token = jwt_encode_handler(payload)
            return APIResponse(0, 'ok', results={
                'username': user.username,
                'token': token
            })
    
    from app import authentication
    
    class CheckAuthUserAPIView(APIView):
        permission_classes = ()
    
        def get(self, request, *args, **kwargs):
            return APIResponse(0, 'ok', results={
                'method': request.method.upper(),
            })
    
        def post(self, request, *args, **kwargs):
            print(request.user)
            print(request.user["username"])
            return APIResponse(0, 'ok', results={
                'username': request.user["username"],
            })
    
        def put(self, request, *args, **kwargs):
            return APIResponse(0, 'ok', results={
                'method': request.method.upper(),
            })
    
        def delete(self, request, *args, **kwargs):
            return APIResponse(0, 'ok', results={
                'method': request.method.upper(),
            })

    9.项目文件夹djangoJWT-->urls.py 如下

    """djangoJWT URL Configuration
    
    The `urlpatterns` list routes URLs to views. For more information please see:
        https://docs.djangoproject.com/en/3.1/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
    from app import views
    from rest_framework_jwt.views import obtain_jwt_token, refresh_jwt_token
    from django.contrib import admin
    from django.urls import path, include
    
    urlpatterns = [
        path('admin/', admin.site.urls),
        # 测试用户是否有效
        path('login', obtain_jwt_token),
        # 获取token
        path('getAuthToken', views.LoginJWTAPIView.as_view()),
        # 验证token 并获取当前用户
        path('checkAuthUser', views.CheckAuthUserAPIView.as_view()),
    ]

    10.生成数据库迁移并创建用户

    python manage.py makemigrations
    python manage.py migrate
    python manage.py createsuperuser

    11.运行效果

     12.运行效果 获取请求的用户

    postman 下载地址:https://www.postman.com/downloads/

    13.后台管理 管理token信息

    参考文章:

    JWT 相关信息介绍:

    https://www.jianshu.com/p/576dbf44b2ae

    DRF JWT的用法:

    https://www.cnblogs.com/neozheng/p/9852888.html

  • 相关阅读:
    Arrays类
    spring boot 整合ehcache
    自定义注解
    图像技术经典会议
    机器学习常见优化器
    TensorFlow学习笔记(一)
    Linux 下 jupyter安装
    学生、课程、分数关系的设计与实现 Hibernate
    Hibernate连接三种数据库的配置(SQL Server、Oracle、MySQL)
    Oracle11g服务详细介绍及哪些服务是必须开启的?
  • 原文地址:https://www.cnblogs.com/wangcongxing/p/13510661.html
Copyright © 2011-2022 走看看