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.eyJ1c2VyX2lkIjoxLCJ1c2VybmFtZSI6Im1hcGVsIiwiZXhwIjoxNjA0MjE5MTk4fQ 第二步:对前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 目录结构
2.1.2 创建所需
- 注意事项
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 效果展示
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