社区微信群开通啦,扫一扫抢先加入社区官方微信群
社区微信群
认证组件
使用方式介绍
定义一个认证类
class UserAuth():
def authenticate_header(self, request):
pass
# 所有的逻辑都在authenticate上写
def authenticate(self, request):
pass
只需要认证的接口里面指定认证类
class BookView(ModelViewSet):
# 指定认证类 UserAuth
authenticate_class = [UserAuth]
# 获取queryset对象
queryset = BookInfo.objects.all()
# 指定序列化类
serializer_class = BookSerializer
导入模块
from django.http import JsonResponse
from rest_framework.views import APIView
from rest_framework.viewsets import ModelViewSet
from rest_framework.exceptions import APIException
url.py
from django.urls import re_path
from .views import UserView, BookView
urlpatterns = [
re_path(r"^books/$", BookView.as_view({
"get": "list",
"post": "create"
})),
re_path(r"^books/(?P<pk>d+)/$", BookView.as_view({
"get": "retrieve",
"put": "update",
"delete": "destroy"
})),
re_path(r"^user/$", UserView.as_view())
]
model.py
from django.db import models
from datetime import datetime
# from django.contrib.auth.models import AbstractUser
class UserInfo(models.Model):
username = models.CharField(max_length=32, verbose_name="用户名")
password = models.CharField(max_length=40, verbose_name="用户密码")
user_type_entry = (
(1, "Delux"),
(2, "SVIP"),
(3, "VVIP")
)
user_type = models.IntegerField(choices=user_type_entry)
class Meta:
db_table = "db_user_info"
verbose_name = "用户信息"
verbose_name_plural = verbose_name
def get_user_type_display(self):
return self.user_type_entry[self.user_type]
class UserToken(models.Model):
# OneToOneField一对一关系
# on_delete=models.CASCADE 当UserInfo数据表里面的数据被删除后UserToken里面的数据也随着被删除
user = models.OneToOneField("UserInfo", on_delete=models.CASCADE, verbose_name="一对一关系")
token = models.CharField(max_length=128, verbose_name="token")
class Meta:
db_table = "db_user_token"
verbose_name = "用户token"
verbose_name_plural = verbose_name
class AuthorInfo(models.Model):
name = models.CharField(max_length=32, verbose_name="作者名称")
age = models.IntegerField(verbose_name="作者年龄")
class Meta:
db_table = "db_author_info"
verbose_name = "作者信息"
verbose_name_plural = verbose_name
def __str__(self):
return self.name
class PublishInfo(models.Model):
name = models.CharField(max_length=32, verbose_name="出版社名称")
city = models.CharField(max_length=32, verbose_name="出版社所在城市")
email = models.EmailField(verbose_name="出版社邮箱")
class Meta:
db_table = "db_publish_info"
verbose_name = "出版社信息"
verbose_name_plural = verbose_name
def __str__(self):
return self.name
class BookInfo(models.Model):
title = models.CharField(max_length=32, verbose_name="书名")
publishDate = models.DateField(default=datetime.now, verbose_name="出版日期")
# max_digits小数位加整数位多少长度 decimal_places小数位长度为2
price = models.DecimalField(max_digits=5, decimal_places=2, verbose_name="图书价格")
publish = models.ForeignKey(PublishInfo, related_name="book", related_query_name="book_query", on_delete=models.CASCADE, verbose_name="出版社")
# ManyToManyField多对多
authors = models.ManyToManyField(AuthorInfo, verbose_name="图书作者")
class Meta:
db_table = "db_book_info"
verbose_name = "图书信息"
verbose_name_plural = verbose_name
def __str__(self):
return self.title
序列化类
from.models import BookInfo
# 第一步: 导入模块
from rest_framework import serializers
from datetime import datetime
class BookSerializer(serializers.ModelSerializer):
class Meta:
# 指定 要操作的模型类
model = BookInfo
# 指定序列化的字段
fields = (
"title",
"price",
"publishDate",
"publish",
"authors",
"author_list",
"publish_name",
"publish_city"
)
# 指定那些字段是只写的
# write_only只写的 (只写的 前端发送数据时要写它, 后端返回数据时没有它)
extra_kwargs = {
"publish": {"write_only": True},
"authors": {"write_only": True}
}
# 自定义的字段
# read_only只读的 (只读的 前端发送数据时不用写它, 后端返回数据时有它)
publish_name = serializers.CharField(max_length=32, read_only=True, source="publish.name")
publish_city = serializers.CharField(max_length=32, read_only=True, source="publish.city")
author_list = serializers.SerializerMethodField()
def get_author_list(self, book_obj):
# 拿到queryset开始循环 [ {}, {}, {}]
authors = []
for author in book_obj.authors.all():
authors.append(author.name)
return authors
token值
import uuid
def generate_token():
# 把uuid残生出来的值转换成字符串, 然后以-切割在以空链接
random_str = str(uuid.uuid4()).replace("-", "")
return random_str
views.py
from django.http import JsonResponse
from rest_framework.views import APIView
from rest_framework.viewsets import ModelViewSet
from rest_framework.exceptions import APIException
from .models import BookInfo, PublishInfo, AuthorInfo, UserInfo, UserToken
from .app_serializers import BookSerializer
from .utils import get_token
# 第一步: 定义认证类
class UserAuth(object):
def authenticate_header(self, request):
pass
# 所有的逻辑都在authenticate上写
def authenticate(self, request):
# 获取token值
user_token = request.GET.get("token", "")
if user_token:
try:
# 查询token值看有没有认证过的token
token = UserToken.objects.get(token=user_token)
# 后面权限会用到
return token.user.username, token.token
except UserToken.DoesNotExist:
raise APIException("没有认证")
else:
raise APIException("没有认证")
class BookView(ModelViewSet):
# 第二步: 指定认证类 UserAuth
authentication_classes = [UserAuth]
queryset = BookInfo.objects.all()
serializer_class = BookSerializer
class UserView(APIView):
def post(self, request):
response = dict()
# 定义需要的用户信息
fields = {"username", "password"}
# 定义一个用户信息字典
user_info = dict()
# request.data是前端发送过来的json数据
# fields.issubset表示 fields是request.data.keys()的子集就显示True, 相反False
if fields.issubset(set(request.data.keys())):
# username = request.data.get("username", "")
# password = request.data.get("password", "")
for key in fields:
# user_info添加个key的键值, request.data把key键的值赋值过去
user_info[key] = request.data[key]
if user_info:
# **user_info字典拆包 拆包成键值对, 而不是整个字典了 列 {"a":"bb"} 拆包成 a="bb"
user_instance = UserInfo.objects.filter(**user_info).first()
if user_instance is not None:
access_token = get_token.generate_token()
# 根据user=user_instance找到了直接更新, 把default的值复制给它
# 没有找到直接创建
# 意思就是有这个token更新token, 没有这个token创建一个token
UserToken.objects.update_or_create(user=user_instance, defaults={
"token": access_token
})
response["status_code"] = 200
response["status_message"] = "登入成功"
response["access_token"] = access_token
response["user_role"] = user_instance.get_user_type_display()
else:
response["status_code"] = 201
response["status_message"] = "登入失败, 用户名或密码错误"
return JsonResponse(response)
else:
return JsonResponse({"error": "参数不完整"})
postman没有传递token值
postman传递token值
多个认证类其他同上, 就视图改变了
# 注意: 如果需要返回什么数据, 请在最后一个认证类中返回, 因为如果在前面返回, 在self._authentication()方法中会对返回值进行判断, 如果不为空, 认证的过程会终止.
class UserAuth2(object):
def authenticate(self ,request):
raise APIException("认证失败")
class UserAuth(object):
def authenticate_header(self, request):
pass
def authenticate(self, request):
# 获取token值
user_token = request.GET.get("token", "")
if user_token:
try:
# 查询token值看有没有认证过的token
token = UserToken.objects.get(token=user_token)
# 后面权限会用到
return token.user.username, token.token
except UserToken.DoesNotExist:
raise APIException("没有认证")
else:
raise APIException("没有认证")
class BookView(ModelViewSet):
# 第二步: 指定认证类 UserAuth
authentication_classes = [UserAuth, UserAuth2]
queryset = BookInfo.objects.all()
serializer_class = BookSerializer
如果不希望每次都写无用的authenticate_header方法, 其他步足同上, 就认证类要继承BaseAuthentication
from rest_framework.authentication import BaseAuthentication
class UserAuth(BaseAuthentication):
def authenticate(self, request):
# 获取token值
user_token = request.GET.get("token", "")
if user_token:
try:
# 查询token值看有没有认证过的token
token = UserToken.objects.get(token=user_token)
# 后面权限会用到
return token.user.username, token.token
except UserToken.DoesNotExist:
raise APIException("没有认证")
else:
raise APIException("没有认证")
REST_FRAMEWORK = {
# 这里可以用列表, 也可以用元祖
'DEFAULT_AUTHENTICATION_CLASSES': [
# 这是路径 本项目的serializer下的authentication_classes下的UserAuth这个认证类
# 这个认证类还是痛上的那个认证类, 没改变
'serializer.authentication_classes.UserAuth',
],
}
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!