ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

django的用户管理与token认证(四)

2026/8/21 9:54:56 拓冰建站 浏览量
django的用户管理与token认证(四)

一、django的用户管理

1.用户认证介绍

django的用户认证主要是用于用户的登陆认证上,如果用户名和密码正确,然后做对应的操作
Django的认证组件使用的默认用户模型类(User)存储用户信息,包括用户名、密码和电子邮件地址。
在执行

python manage.py  makemigrations

在数据库中会生成几张默认的表

mysql> show tables;
+----------------------------+
| Tables_in_mydatabase       |
+----------------------------+
| auth_group                 |
| auth_group_permissions     |
| auth_permission            |
| auth_user                  |
| auth_user_groups           |
| auth_user_user_permissions |
| django_admin_log           |
| django_content_type        |
| django_migrations          |
| django_session			 |
+----------------------------+

其中auth_user就是存储用户的表,默认这张表中没有用户。表结构如下:

mysql> desc auth_user;
+--------------+--------------+------+-----+---------+----------------+
| Field        | Type         | Null | Key | Default | Extra          |
+--------------+--------------+------+-----+---------+----------------+
| id           | int(11)      | NO   | PRI | NULL    | auto_increment |
| password     | varchar(128) | NO   |     | NULL    |                |
| last_login   | datetime(6)  | YES  |     | NULL    |                |
| is_superuser | tinyint(1)   | NO   |     | NULL    |                |
| username     | varchar(150) | NO   | UNI | NULL    |                |
| first_name   | varchar(150) | NO   |     | NULL    |                |
| last_name    | varchar(150) | NO   |     | NULL    |                |
| email        | varchar(254) | NO   |     | NULL    |                |
| is_staff     | tinyint(1)   | NO   |     | NULL    |                |
| is_active    | tinyint(1)   | NO   |     | NULL    |                |
| date_joined  | datetime(6)  | NO   |     | NULL    |                |
+--------------+--------------+------+-----+---------+----------------+
11 rows in set (0.00 sec)

2.用户的Models类(User)

django使用User模型类来对应auth_user表,我们可以通过User类来对auth_user表进行操作
这里单独创建一个user应用

django-admin startapp user

2.1 创建序列化器

在user应用下新建serializers.py文件

from rest_framework.serializers import ModelSerializer
# 导入django提供的 User 数据库models类
from django.contrib.auth.models import Userclass auth_user_serializer(ModelSerializer):class Meta:model = User# 根据需求选择一些必要的字段进行序列化。fields = ["id","username","is_active","is_superuser","last_login"]

2.2 设置路由

# 设置总路由:
urlpatterns = [path('user/',include("user.urls"))
]# 设置子路由:
from django.urls import  path
from .views import *urlpatterns = [path('',userview.as_view())
]

2.3 新增用户和获取所有用户

编辑views.py文件,实现获取所有用户和创建用户的功能

from django.contrib.auth.models import User
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework.generics import ListAPIView,RetrieveAPIView
from .serializers import auth_user_serializerclass userview(ListAPIView):# 前两行代码 主要是实现获取所有用户列表queryset = User.objects.all()serializer_class = auth_user_serializer# 实现添加用户的功能,之所以没有使用CreateAPIViews视图类。是因为User类提供了两个创建用户的方法:# (1)create_user() : 创建普通用户# (2)create_superuser(): 创建管理员用户def post(self,request):data = request.dataser_data = auth_user_serializer(data=data)if ser_data.is_valid():# 这里只是创建了普通用户,这里可以根据用户传递的参数决定创建普通用户还是管理员User.objects.create_user(**ser_data.validated_data)return Response("{}用户创建成功".format(data['username']),status=200)else:return Response(ser_data.errors,status=405)

2.4 测试

测试创建用户是否成功

POST http://127.0.0.1:8000/user/  提交数据  {"username":"admin","passwowrd":"123"}

测试获取所有用户是否成功

http://127.0.0.1:8000/user/# 获取数据如下
[{"id": 1,"username": "admin","is_active": true,"is_superuser": false,"last_login": null}
]

查看数据库中是否有数据。可以看出确实User模型类对应的是auth_user表

mysql> select * from auth_user\G
*************************** 1. row ***************************id: 1password: pbkdf2_sha256$260000$0AYS0xx7BRn7P9P46a3Q8Y$V9aVCZ6jOgVWK3e8ZF/PkPu3Wldoc6DzrXRB9c5tFfM=last_login: NULL
is_superuser: 0username: adminfirst_name: last_name: email: is_staff: 0is_active: 1date_joined: 2024-02-22 02:27:08.195161
1 row in set (0.00 sec)

2.5 修改用户密码

from django.contrib.auth.models import User
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework.generics import ListAPIView,RetrieveAPIView
from .serializers import auth_user_serializerclass userview(ListAPIView):queryset = User.objects.all()serializer_class = auth_user_serializerdef post(self,request):data = request.dataser_data = auth_user_serializer(data=data)if ser_data.is_valid():User.objects.create_user(**ser_data.validated_data)User.objects.create_superuser()return Response("{}用户创建成功".format(data['username']),status=200)else:return Response(ser_data.errors,status=405)# 修改密码功能def put(self,request):data = request.datafield_list = ["username","password"]# 判断是否确实必须字段for field in field_list:if not field in data:return  Response({"message": "'{}是必填写字段'.format(field)"})try:# 这里之所以使用用户名过滤而没有使用ID,是因为用户名有唯一性约束query_res = User.objects.get(username=data["username"])except Exception:return Response({"message":"用户不存在,无法修改密码"},status=405)try:query_res.set_password(data["password"])query_res.save()except Exception:return Response({"message":"密码修改失败"},status=500)else:return Response({"message": "密码修改成功!"},status=200)

2.6 删除用户功能

from django.contrib.auth.models import User
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework.generics import ListAPIView,RetrieveAPIView
from .serializers import auth_user_serializerclass userview(ListAPIView):queryset = User.objects.all()serializer_class = auth_user_serializerdef post(self,request):data = request.dataser_data = auth_user_serializer(data=data)if ser_data.is_valid():User.objects.create_user(**ser_data.validated_data)User.objects.create_superuser()return Response("{}用户创建成功".format(data['username']),status=200)else:return Response(ser_data.errors,status=405)# 修改密码功能def put(self,request):data = request.datafield_list = ["username","password"]# 判断是否确实必须字段for field in field_list:if not field in data:return  Response({"message": "'{}是必填写字段'.format(field)"})try:# 这里之所以使用用户名过滤而没有使用ID,是因为用户名有唯一性约束query_res = User.objects.get(username=data["username"])except Exception:return Response({"message":"用户不存在,无法修改密码"},status=405)try:query_res.set_password(data["password"])query_res.save()except Exception:return Response({"message":"密码修改失败"},status=500)else:return Response({"message": "密码修改成功!"},status=200)# 删除用户功能def delete(self,request):data = request.dataif "username" in data:try:query_data = User.objects.get(username=data["username"])except Exception:return Response({"message": "{}用户不存在".format(data["username"])},status=500)else:query_data.is_active = 0query_data.save()return Response({"message": "{}用户删除成功".format(data["username"])},status=200)else:return Response({"message": "字段错误,username为必填字段"})

2.7 命令行操作

在命令创建管理员,注意一定要先迁移数据库,也就是说要在数据库中必须要生成auth_user表

python manage.py createsuperuser

二、用户认证

在上边已经了解到了django的用户模型类(User).然后通过自己编写的功能实现用户的增删改查。那么用户访问的时候就可以通过User模型类创建好的用户访问django的其它接口。
那么用户携带的用户名和密码到底对不对呢?django是怎么判断的?,当用户携带的用户名和密码正确,后续的操作又该做什么?这就需要用到了 “DRF的认证”
DRF的认证一共有4种,如下:

认证方式作用
BasicAuthentication客户端用户名密码认证
SessionAuthentication采用sessionid的方式进行校验。
TokenAuthentication采用token值进行校验
RemoteUserAuthentication不经常用

1.源码解析

在DRF中使用APIView视图类,其中提供了一个认证配置.
打开APIView类,可以看到

class APIView(View):............# 这里可以看到,有一authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES

查看api_settings,它是APISettings的实例化对象。在实力化的时候将DEFAULTS(这是一个字典)配置传递了进去。

api_settings = APISettings(None, DEFAULTS, IMPORT_STRINGS)

DEFAULTS对象中有一个配置DEFAULT_AUTHENTICATION_CLASSES。点击DEFAULTS查看

  'DEFAULT_AUTHENTICATION_CLASSES': ['rest_framework.authentication.SessionAuthentication','rest_framework.authentication.BasicAuthentication'],

DRF默认使用了session和Basic认证

2.基本认证(BasicAuthentication)

测试视图类如下:

from rest_framework.views import APIView,Response
from rest_framework.authentication import BasicAuthentication
from rest_framework.permissions import IsAuthenticatedclass test(APIView):# 选择使用基本认证类型,也就是用户名和密码验证authentication_classes = [BasicAuthentication]# 配置验证用户是否登录成功,如果陈成功,执行下边函数,不成功抛出异常permission_classes = [IsAuthenticated]def get(self,request):return Response({"message":"访问成功!"},status=200)

浏览器访问对应路由,就会自动弹出认证弹窗。让用户输入用户名和密码。如果输入错误,会让用户重新输入用户名和密码。
此验证方式尽量不要用于生产环境。安全系数较低

三.用户认证-token

简介:
JSON Web Token不再维护,所以在不使用JWT组件了。
官网建议使用simpleJWT组件

1.配置路由

1.1总路由urls.py配置

from django.urls import path,includeurlpatterns = [path('app1/', include('app1.urls')),
]

1.2 子路由urls.py配置

from django.urls import path
from .views import *urlpatterns = [path('', test.as_view()),
]

2.配置视图类

from rest_framework.views import APIView,Responseclass test(APIView):def get(self,request):return Response({"message":"访问成功!!!"},status=200)

3.访问测试

GET http://127.0.0.1:8000/app1/

返回结果:

{"message": "访问成功!!!"
}

前3步准备了一个最基础的DRF视图类和对应路由

4.安装JWT组件

pip install djangorestframework-simplejwt

5.配置使用JWT认证

在settings.py文件中,加入以下配置.
这里的配置是全局配置,对所有接口都生效。
DEFAULT_AUTHENTICATION_CLASSES:配置django的验证方式
DEFAULT_PERMISSION_CLASSES: 配置django的验证动作

REST_FRAMEWORK = {'DEFAULT_AUTHENTICATION_CLASSES': [# 使用rest_framework_simplejwt(token)方式做为验证方式。'rest_framework_simplejwt.authentication.JWTAuthentication',],'DEFAULT_PERMISSION_CLASSES': [# 用户访问所有接口的时候,都需要验证用户是否携带token,并且是否正确。正确才可以访问。'rest_framework.permissions.IsAuthenticated'  ],
}

5.1 测试

这里配置了使用JWT验证后,在测试访问之前的接口

GET http://127.0.0.1:8000/app1/

返回结果:异常提示 “未提供身份验证凭据”

{"detail": "Authentication credentials were not provided."
}

6.配置token路由

因为token是全局性的,面向所有视图类进行验证,所以这里就配置在总路由的urls.py文件中了,

from django.urls import path,include# 导入simplejwt的视图类
from rest_framework_simplejwt.views import (TokenObtainPairView,TokenRefreshView,TokenVerifyView
)urlpatterns = [# 业务的接口路由path('app1/', include('app1.urls')),# 获取token的路由path('api/token/',TokenObtainPairView.as_view()),# 获取单独刷新access token的路由path('api/refresh/',TokenRefreshView.as_view()),# 验证token是否有效的路由path('api/token/verify/',TokenVerifyView.as_view())]

6.1 获取token

在 “一” 中已经讲述了User的使用,并且创建合法用户。在获取token时需要使用User模型类创建的用户作为验证。

POST http://127.0.0.1:8000/api/token/POST数据为:
{"username": "admin","password": "admin"
}

返回结果:

{"refresh": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlbl90eXBlIjoicmVmcmVzaCIsImV4cCI6MTcwODkyODM4NCwiaWF0IjoxNzA4ODQxOTg0LCJqdGkiOiIyMTI5ZTdkZGIxZDY0YmVjYWY5NDU0YTY0MjkwNDM3NSIsInVzZXJfaWQiOjF9.sKp2TKv9CEH0xfKK5xLMiFq5eJvlZPFswIvxaK8eAxo","access": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNzA4ODQyMjg0LCJpYXQiOjE3MDg4NDE5ODQsImp0aSI6IjM0ZDc3NWVhMDI1NjQ2NzE4YWZmYzRjNjU4Y2QzZGU1IiwidXNlcl9pZCI6MX0.7xsMmaagvYxNVAbqapY_40stnXPtOHPIGoNLuJSpYIo"
}

6.2 token类型

上述结果中返回了两个token

refresh: 这个token是用来刷新access token的
access: 这个token是 用户访问业务接口需要携带的token.用户携带的这个token有效,才可以访问接口

6.3 携带token访问接口

GET  http://127.0.0.1:8000/app1/# 在header头部中增加
key: Authorization
值:  Bearer ”上边获取的access对应的token值“

返回结果:

{"message": "访问成功!!!"
}

7.配置JWT的过期时间

依然是在settings.py文件中进行配置。
为了测试这两个配置的时间都比较短

# 注意,在配置文件中导入datetime模块
import datetimeSIMPLE_JWT = {# token有效时长'ACCESS_TOKEN_LIFETIME': datetime.timedelta(seconds=20),# token刷新后的有效时间'REFRESH_TOKEN_LIFETIME': datetime.timedelta(seconds=3000),
}

一般生产环境token的配置是refresh的时间长一点,token的时间短一点,比如:

SIMPLE_JWT = {# token有效时长'ACCESS_TOKEN_LIFETIME': datetime.timedelta(minutes=30),# token刷新后的有效时间'REFRESH_TOKEN_LIFETIME': datetime.timedelta(days=1),
}

7.1 获取token

POST http://127.0.0.1:8000/api/token/POST数据为:
{"username": "admin","password": "admin"
}

7.2 携带token访问接口

GET http://127.0.0.1:8000/app1/

返回接口:

{"message": "访问成功!!!"
}

7.3 使用过期token访问

因为上边设置了token的有效期是20秒。等待20秒左右。依然使用刚才的access token访问接口,发现返回接口如下:

{"detail": "Given token not valid for any token type","code": "token_not_valid","messages": [{"token_class": "AccessToken","token_type": "access","message": "Token is invalid or expired"}]
}

token已经过期了

四、token的刷新与验证

在token的路由中,一共配置了3个关于token的路由,上边只是使用了一个:api/token/。这里讲解剩下的两个

1.刷新token

上述也讲过了refresh的值是专门用来刷新token的值的。

POST http://127.0.0.1:8000/api/refresh/# POST数据为:
{"refresh": "获取到的值"
}

返回结果:

{"access": "值"
}

使用这个接口可以不断刷新access 的值

2.refresh的过期时间

SIMPLE_JWT = {'ACCESS_TOKEN_LIFETIME': datetime.timedelta(seconds=20),# 将这里的过期时间调整为30s'REFRESH_TOKEN_LIFETIME': datetime.timedelta(seconds=30),
}

2.1 重新获取resfresh token

POST http://127.0.0.1:8000/api/token/POST数据为:
{"username": "admin","password": "admin"
}

2.2 携带refresh token刷新access

POST http://127.0.0.1:8000/api/refresh/# POST数据为:
{"refresh": "刚刚获取到过期时间为30s的refresh值"
}

等待30s后,返回结果如下:

{"detail": "Token is invalid or expired","code": "token_not_valid"
}

3.token的验证

POST http://127.0.0.1:8000/api/token/verify/# post数据为:
{"token": "access的值"
}

如果验证成功,并且没有过期,返回空字典{}。
如果过期返回如下:

{"detail": "Token is invalid or expired","code": "token_not_valid"
}