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的用法: