编写登录功能引出认证,权限,频率:
前端通过接口测试工具Postman将用户名和密码通过HTTP请求发送至Django框架
models.py
from django.db import models
# Create your models here.
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表中含有请求中携带的数据,username,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)
# Perform content negotiation and store the accepted info on the request
neg = self.perform_content_negotiation(request)
request.accepted_renderer, request.accepted_media_type = neg
# Determine the API version, if versioning is in use.
version, scheme = self.determine_version(request, *args, **kwargs)
request.version, request.versioning_scheme = version, scheme
# Ensure that the incoming request is permitted
"""认证"""
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('books/',views.BookAPIView.as_view({'get':'login'})),
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
# -注意:取get提交的数据,不要从request.GET中取,从request.query_params中取
token = request.query_params.get('token')
print(token)
# 去数据库查询,token是否存在,存在表示登录了,不存在表示为登录
user_token = models.UserToken.objects.filter(token=token).first()
if user_token:
# 943ffc46-3826-47f9-a4de-ea9cd4bd23d6
print(user_token.token)
# User object (1),模型类对象
print(user_token.user)
# junjie
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 = []
源码分析:
# APIView--->dispathch方法的-----》self.initial(request, *args, **kwargs)(497行)---》APIView的initial方法----》有三句话
self.perform_authentication(request) # 认证
self.check_permissions(request) # 权限
self.check_throttles(request) # 频率
# self.perform_authentication(request)----》新的request对象的.user(是个方法,包装成了数据数属性)---》新的Request类中找到了user方法---》self._authenticate()-----》Request类中的_authenticate
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
# self.authenticators,self是Request类的对象,authenticators属性是Request这个类在实例化的时候,传入的
# Request类在实例化的时候代码
return Request(
request,
parsers=self.get_parsers(),
authenticators=self.get_authenticators(), # 要看的
negotiator=self.get_content_negotiator(),
parser_context=parser_context
)
# self.get_authenticators()是APIView的方法
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)
)
# message 自定义报错信息
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一定要写,字符串对应到settings.py中配置REST_FRAMEWORK。
scope = 'ip_Throttle'
def get_cache_key(self, request, view):
# 请求头所包含的信息在META中
# 127.0.0.1
print(request.META.get('REMOTE_ADDR'))
return request.META.get('REMOTE_ADDR')
#### settings.py,配置文件中一定要配置。
REST_FRAMEWORK = {
# (一分钟访问三次),key要跟类中的scope对应
'DEFAULT_THROTTLE_RATES':{
'ip_Throttle':'3/m'
}
}
settings.py ,全局配置。
REST_FRAMEWORK = {
# (一分钟访问三次),key要跟类中的scope对应
'DEFAULT_THROTTLE_RATES':{
'ip_Throttle':'3/m'
},
# 全局配置
'DEFAULT_THROTTLE_CLASSES': (
'app01.auth.MyThrottle',
),
}
views.py,视图类中配置
class BookAPIView(ViewSetMixin, ListAPIView):
# 局部使用
throttle_classes = [MyThrottle,]
标签:get,models,self,request,认证,token,user,权限,drf
From: https://www.cnblogs.com/HeroZhang/p/18094147