认证组件
-
认证组件
-
使用方式介绍
-
定义一个认证类
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方法
-
如果不希望每次都写无用的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("没有认证")
全局认证类
- 视图里不用指定认证类了, 就加了2步, 其他都不变
- 本项目settings.py
REST_FRAMEWORK = { # 这里可以用列表, 也可以用元祖 'DEFAULT_AUTHENTICATION_CLASSES': [ # 这是路径 本项目的serializer下的authentication_classes下的UserAuth这个认证类 # 这个认证类还是痛上的那个认证类, 没改变 'serializer.authentication_classes.UserAuth', ], } - 把认证类放到这个路径下就好了, 千万记住, 视图上面就不需要添加认证了, 把视图上的
authentication_classes = [UserAuth] 注释掉
- 本项目settings.py
认证组件源码



浙公网安备 33010602011771号