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

posted @ 2020-08-15 22:18  汪丛兴  阅读(351)  评论(0编辑  收藏  举报