1.1 原理及其实现
1.1.1 与传统不同之处
1)基于传统的token验证
用户登录,服务端给返回token,并将token保存在服务端 以后用户再来访问时,需要携带token,服务端获取token后,再去数据库中获取token进行校验。
2)jwt
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