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 User

class 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_serializer

class 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.data
        ser_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: 1
    password: pbkdf2_sha256$260000$0AYS0xx7BRn7P9P46a3Q8Y$V9aVCZ6jOgVWK3e8ZF/PkPu3Wldoc6DzrXRB9c5tFfM=
  last_login: NULL
is_superuser: 0
    username: admin
  first_name: 
   last_name: 
       email: 
    is_staff: 0
   is_active: 1
 date_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_serializer

class userview(ListAPIView):
    queryset = User.objects.all()
    serializer_class = auth_user_serializer

    def post(self,request):
        data = request.data
        ser_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.data
        field_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_serializer

class userview(ListAPIView):
    queryset = User.objects.all()
    serializer_class = auth_user_serializer

    def post(self,request):
        data = request.data
        ser_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.data
        field_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.data
        if "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 = 0
                query_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 IsAuthenticated



class 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,include

urlpatterns = [
    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,Response

class 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 datetime

SIMPLE_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"
}

你可能感兴趣的:(pyhton框架,django,sqlite,数据库)