编写登录功能引出认证,权限,频率:
前端通过接口测试工具Postman将用户名和密码通过HTTP请求发送至Django框架
models.py
| from django.db import models |
| |
| |
| |
| class Books(models.Model): |
| name = models.CharField(max_length=32) |
| price = models.IntegerField() |
| publish = models.ForeignKey(to='Publish', on_delete=models.DO_NOTHING) |
| |
| |
| class Publish(models.Model): |
| name = models.CharField(max_length=32) |
| addr = models.CharField(max_length=32) |
| |
| |
| class User(models.Model): |
| username = models.CharField(max_length=32) |
| password = models.CharField(max_length=32) |
| |
| |
| """ |
| 用户登录生成随机字符串,响应中携带字符串,发送至服务端后,服务端做逻辑判断。 |
| 一般一对一为垂直分表。数据库优化的手段 |
| """ |
| class UserToken(models.Model): |
| user = models.OneToOneField(to='User', on_delete=models.CASCADE) |
| token = models.CharField(max_length=64) |
模型层拓展:
views.py
| from rest_framework.views import APIView |
| from . import models |
| import uuid |
| from rest_framework.response import Response |
| |
| |
| class UserAPIView(APIView): |
| def post(self,request,*args,**kwargs): |
| back_dic = {'code':200,'msg':''} |
| username = request.data.get('username') |
| password = request.data.get('password') |
| |
| user = models.User.objects.filter(username=username,password=password).first() |
| print(user) |
| |
| if user: |
| """ |
| 有用户的情况下 |
| 登录成功之后生成token |
| user=user,根据user查询,查询不到使用defaults新增,查询到更新。 |
| """ |
| |
| token = str(uuid.uuid4()) |
| models.UserToken.objects.update_or_create(defaults={'token': token}, user=user) |
| back_dic['msg'] = '随机字符串更新' |
| back_dic['token'] = token |
| else: |
| back_dic['code'] = 201 |
| back_dic['msg'] = '用户名或者密码错误' |
| return Response(back_dic) |

此时客户端发送post请求,后端验证正确会产生一条新的token覆盖原有的token。
认证,权限,频率所在位置。
| |
| APIView --> dispath(重写了request方法) --> initial(认证,权限,频率) |
| def initial(self, request, *args, **kwargs): |
| """ |
| Runs anything that needs to occur prior to calling the method handler. |
| """ |
| self.format_kwarg = self.get_format_suffix(**kwargs) |
| |
| |
| neg = self.perform_content_negotiation(request) |
| request.accepted_renderer, request.accepted_media_type = neg |
| |
| |
| version, scheme = self.determine_version(request, *args, **kwargs) |
| request.version, request.versioning_scheme = version, scheme |
| |
| |
| """认证""" |
| self.perform_authentication(request) |
| """权限""" |
| self.check_permissions(request) |
| """频率""" |
| self.check_throttles(request) |
认证类的编写
之前提到过,如果代码不属于视图层就另开一个py文件。
认证:只有携带token随机字符串才可以访问某个路由。
urls.py
| |
| from app01 import views |
| from rest_framework.routers import SimpleRouter, DefaultRouter |
| from django.urls import path, include |
| |
| router = SimpleRouter() |
| router.register('books', views.BookAPIView) |
| |
| urlpatterns = [ |
| |
| path('', include(router.urls)), |
| path('login/', views.UserAPIView.as_view()) |
| ] |
views.py
| from .auth import LoginAuth |
| from rest_framework.generics import ListAPIView |
| from rest_framework.viewsets import ViewSetMixin |
| |
| |
| class BookAPIView(ViewSetMixin, ListAPIView): |
| queryset = Books.objects.all() |
| serializer_class = BookSerializers |
| |
| authentication_classes = [LoginAuth] |
auth.py
| """认证类""" |
| from rest_framework.authentication import BaseAuthentication |
| from . import models |
| from rest_framework.exceptions import AuthenticationFailed |
| |
| class LoginAuth(BaseAuthentication): |
| def authenticate(self,request): |
| |
| |
| |
| token = request.query_params.get('token') |
| print(token) |
| |
| user_token = models.UserToken.objects.filter(token=token).first() |
| if user_token: |
| |
| print(user_token.token) |
| |
| print(user_token.user) |
| |
| print(user_token.user.username) |
| return user_token.user,user_token.token |
| raise AuthenticationFailed('您没有登录,认证失败') |

此时想要访问books必须携带token才可以访问。
认证类的全局和局部使用
全局配置:
| REST_FRAMEWORK = { |
| 'DEFAULT_AUTHENTICATION_CLASSES':[ |
| 'app01.auth.LoginAuth' |
| ] |
| } |
局部禁用配置:
| class UserAPIView(APIView): |
| authentication_classes = [] |
源码分析:
| |
| self.perform_authentication(request) |
| self.check_permissions(request) |
| self.check_throttles(request) |
| |
| |
| |
| |
| def _authenticate(self): |
| for authenticator in self.authenticators: |
| try: |
| user_auth_tuple = authenticator.authenticate(self) |
| except exceptions.APIException: |
| self._not_authenticated() |
| raise |
| |
| if user_auth_tuple is not None: |
| self._authenticator = authenticator |
| self.user, self.auth = user_auth_tuple |
| return |
| |
| |
| |
| |
| |
| return Request( |
| request, |
| parsers=self.get_parsers(), |
| authenticators=self.get_authenticators(), |
| negotiator=self.get_content_negotiator(), |
| parser_context=parser_context |
| ) |
| |
| |
| |
| def get_authenticators(self): |
| |
| return [auth() for auth in self.authentication_classes] |
| |
| return [LoginAuth(),] |
权限组件
导入模块
| from rest_framework.permissions import BasePermission |
auth.py
| from rest_framework.permissions import BasePermission |
| |
| class UserPermission(BasePermission): |
| def has_permission(self, request, view): |
| """ |
| 认证类走完再走权限类,所以request.user可以拿到当前登录用户 |
| """ |
| if request.user.user_type == 1: |
| return True |
| return False |
全局配置使用:
| REST_FRAMEWORK = { |
| 'DEFAULT_PERMISSION_CLASSES': [ |
| 'app01.auth.UserPermission', |
| ], |
| } |
局部使用:
| class BookAPIView(ViewSetMixin, ListAPIView): |
| queryset = Books.objects.all() |
| serializer_class = BookSerializers |
| |
| authentication_classes = [LoginAuth] |
| |
| permission_classes = [UserPermission] |
| ```python |
| |
| ```python |
| permission_classes = [] |
权限类源码解读
| APIView --> dispath --> initial --> self.check_permissions(request) --》 |
| def check_permissions(self, request): |
| """ |
| for permission in self.get_permissions(),self.get_permissions()在哪? |
| 通过源码可以看到是权限类的全局和局部配置。局部有优先局部,局部没有定义取全局,全局没有去内置。 |
| def get_permissions(self): |
| return [permission() for permission in self.permission_classes] |
| """ |
| for permission in self.get_permissions(): |
| """ |
| 对应到auth.py中的权限类,如果为True,代码正常执行,如果为False,执行self.permission_denied |
| """ |
| if not permission.has_permission(request, self): |
| self.permission_denied( |
| request, |
| """ |
| 反射,现在auth.py中的权限类找massage. |
| """ |
| message=getattr(permission, 'message', None), |
| code=getattr(permission, 'code', None) |
| ) |
| |
| def permission_denied(self, request, message=None, code=None): |
| """ |
| If request is not permitted, determine what kind of exception to raise. |
| """ |
| if request.authenticators and not request.successful_authenticator: |
| raise exceptions.NotAuthenticated() |
| raise exceptions.PermissionDenied(detail=message, code=code) |
auth.py
| class UserPermission(BasePermission): |
| def has_permission(self, request, view): |
| """ |
| 认证类走完再走权限类,所以request.user可以拿到当前登录用户 |
| """ |
| if request.user.user_type == 1: |
| return True |
| return False |
频率组件的使用
频率的作用: 可以对接口访问的频次进行限制,以减轻服务器压力。一般用于付费购买次数,投票等场景使用.
**导入模块: **
| from rest_framework.throttling import SimpleRateThrottle |
auth.py,写一个类,继承基类,重写get_cache_key方法
| |
| class MyThrottle(SimpleRateThrottle): |
| |
| scope = 'ip_Throttle' |
| |
| def get_cache_key(self, request, view): |
| |
| |
| print(request.META.get('REMOTE_ADDR')) |
| return request.META.get('REMOTE_ADDR') |
| |
| |
| REST_FRAMEWORK = { |
| |
| 'DEFAULT_THROTTLE_RATES':{ |
| 'ip_Throttle':'3/m' |
| } |
| } |
settings.py ,全局配置。
| REST_FRAMEWORK = { |
| |
| 'DEFAULT_THROTTLE_RATES':{ |
| 'ip_Throttle':'3/m' |
| }, |
| |
| 'DEFAULT_THROTTLE_CLASSES': ( |
| 'app01.auth.MyThrottle', |
| ), |
| } |
views.py,视图类中配置
| class BookAPIView(ViewSetMixin, ListAPIView): |
| |
| throttle_classes = [MyThrottle,] |



【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了