Django Rest Framwork的认证组件 权限组件以及频率组件
提示:
再看这篇博客之前 最好先看看关于CBV FBV的那篇博客 看完之后再来理解源码以及流程就相对方便多了 https://www.cnblogs.com/wakee/p/12553402.html
本片博客部分参考:https://www.cnblogs.com/wupeiqi/articles/7805382.html
1 DRF简介
''' 基于cbv完成满足restful规范的接口 '''
2 安装drf
pip install djangorestframework
# 安装好之后再settings.py注册app: INSTALLED_APPS = [..., 'rest_framework']
3 认证组件
组件中有俩种获取数据的方式 1 request.data.get() 2 request._request.GET.get() 区别是request.data是前端放在body中的数据 request._request.GET.get()是get请求带的参数
# 对用户进行登录认证
class AuthView(APIView):
def post(self,request,*args,**kwargs):
ret = {
'code':1000,
"msg":None
}
# 从post 里面取数据
print(request.data)
# 获取数据
username = request.data.get("username")
password = request.data.get("password")
class Authtication(BasicAuthentication):
def authenticate(self,request):
# 获取数据
print(request.data) # body中的数据
token = request._request.GET.get("token")
print(token)
3.1 authentication
"""
系统:session认证
rest_framework.authentication.SessionAuthentication
ajax请求通过认证:
cookie中要携带 sessionid、csrftoken,请求头中要携带 x-csrftoken
第三方:jwt认证
rest_framework_jwt.authentication.JSONWebTokenAuthentication
ajax请求通过认证:
请求头中要携带 authorization,值为 jwt空格token
自定义:基于jwt、其它
1)自定义认证类,继承BaseAuthentication(或其子类),重写authenticate
2)authenticate中完成
拿到认证标识 auth
反解析出用户 user
前两步操作失败 返回None => 游客
前两步操作成功 返回user,auth => 登录用户
注:如果在某个分支抛出异常,直接定义失败 => 非法用户
"""
3.2源码流程
dispatch
--封装request
--获取定义的认证类(全局/局部),通过列表生成式创建对象
--initial
--perform_auhentication
--request.user(内部循环列表生成式创建的对象)
具体源码
# 第一步
def dispatch(self, request, *args, **kwargs):
"""
`.dispatch()` is pretty much the same as Django's regular dispatch,
but with extra hooks for startup, finalize, and exception handling.
"""
self.args = args
self.kwargs = kwargs
# 对原生的request进行加工,看第二步
request = self.initialize_request(request, *args, **kwargs) # 点击initialize_request进入第二步看request
self.request = request
self.headers = self.default_response_headers # deprecate?
try:
self.initial(request, *args, **kwargs)
# Get the appropriate handler method
if request.method.lower() in self.http_method_names:
handler = getattr(self, request.method.lower(),
self.http_method_not_allowed)
else:
handler = self.http_method_not_allowed
response = handler(request, *args, **kwargs)
except Exception as exc:
response = self.handle_exception(exc)
self.response = self.finalize_response(request, response, *args, **kwargs)
return self.response
# 第二步
def initialize_request(self, request, *args, **kwargs):
"""
Returns the initial request object.
"""
parser_context = self.get_parser_context(request)
# 对request进行了加工 不仅有原生request 还有其他的类对象比如authentication_classes
return Request(
request,
parsers=self.get_parsers(),
authenticators=self.get_authenticators(), # 点击get_authenticators()进入第三步
negotiator=self.get_content_negotiator(),
parser_context=parser_context
)
# 第三步
def get_authenticators(self):
"""
Instantiates and returns the list of authenticators that this view can use.
"""
# 谁调用他就拿到那个类的对象
return [auth() for auth in self.authentication_classes] # 点击authentication_classes 第四步
# 第四步:
class APIView(View):
# The following policies may be set at either globally, or per-view.
renderer_classes = api_settings.DEFAULT_RENDERER_CLASSES
parser_classes = api_settings.DEFAULT_PARSER_CLASSES
authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES # 点击后看的部分
throttle_classes = api_settings.DEFAULT_THROTTLE_CLASSES
permission_classes = api_settings.DEFAULT_PERMISSION_CLASSES
content_negotiation_class = api_settings.DEFAULT_CONTENT_NEGOTIATION_CLASS
metadata_class = api_settings.DEFAULT_METADATA_CLASS
versioning_class = api_settings.DEFAULT_VERSIONING_CLASS
# 第五步
def dispatch(self, request, *args, **kwargs):
"""
`.dispatch()` is pretty much the same as Django's regular dispatch,
but with extra hooks for startup, finalize, and exception handling.
"""
self.args = args
self.kwargs = kwargs
# 对原生的request进行加工 ,其实就是
request = self.initialize_request(request, *args, **kwargs) # 点击initialize_request进入第二步看request
self.request = request
self.headers = self.default_response_headers # deprecate?
try:
self.initial(request, *args, **kwargs) # 点击进入第六步
# Get the appropriate handler method
if request.method.lower() in self.http_method_names:
handler = getattr(self, request.method.lower(),
self.http_method_not_allowed)
else:
handler = self.http_method_not_allowed
response = handler(request, *args, **kwargs)
except Exception as exc:
response = self.handle_exception(exc)
self.response = self.finalize_response(request, response, *args, **kwargs)
return self.response
# 第六步
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)
# 第七步
def perform_authentication(self, request):
"""
Perform authentication on the incoming request.
Note that if you override this and simply 'pass', then authentication
will instead be performed lazily, the first time either
`request.user` or `request.auth` is accessed.
"""
request.user # 点击user进入下一步
# 第八步
@property
def user(self):
"""
Returns the user associated with the current request, as authenticated
by the authentication classes provided to the request.
"""
if not hasattr(self, '_user'):
with wrap_attributeerrors():
self._authenticate() # 点击进入下一步
return self._user
# 第九步
def _authenticate(self):
# 遍历拿到一个个认证器,进行认证
# self.authenticators配置的一堆认证类产生的认证类对象组成的 list
for authenticator in self.authenticators:
try:
# 认证器(对象)调用认证方法authenticate(认证类对象self, request请求对象)
# 返回值:登陆的用户与认证的信息组成的 tuple(request.user,request.auth)
# 该方法被try包裹,代表该方法会抛异常,抛异常就代表认证失败
user_auth_tuple = authenticator.authenticate(self)
except exceptions.APIException:
self._not_authenticated()
raise
# 返回值的处理
if user_auth_tuple is not None:
self._authenticator = authenticator
# 如何有返回值,就将 登陆用户 与 登陆认证 分别保存到 request.user、request.auth
self.user, self.auth = user_auth_tuple
return
# 如果返回值user_auth_tuple为空,代表认证通过,但是没有 登陆用户 与 登陆认证信息,代表游客
self._not_authenticated()
3.3 自定登录认证
models.py
from django.db import models
class Userinfo(models.Model):
user_type_choice ={
(1,'普通用户'),
(2,'VIP用户'),
(3,'SVIP用户')
}
user_type = models.IntegerField(choices=user_type_choice)
username = models.CharField(max_length=32)
password = models.CharField(max_length=64)
# 用户token表
class UserToken(models.Model):
user = models.OneToOneField(to="Userinfo",on_delete=True)
token = models.CharField(max_length=64)
views.py
from django.shortcuts import render
from rest_framework.views import APIView
from api import models
from django.http import JsonResponse,HttpResponse
# 生成随机字符创(token) 并且进行更新
def md5(username):
import hashlib
import time
ctime = str(time.time())
m = hashlib.md5(bytes(username,encoding="utf-8") )
m.update(bytes(ctime,encoding="utf-8"))
return m.hexdigest()
# 对用户进行登录认证
class AuthView(APIView):
def get(self,request):
return HttpResponse("GET请求")
def post(self,request,*args,**kwargs):
ret = {
'code':1000,
"msg":None
}
# 从post 里面取数据
# 注意 获取值是request.data.get()来获取
print(request.data) # 这是前端带来的数据
username = request.data.get("username")
password = request.data.get("password")
print(username,password)
obj = models.Userinfo.objects.filter(username=username,password=password).first()
if not obj:
ret["code"] = 1001
ret["msg"] = "用户名或密码错误"
return JsonResponse(ret)
if obj:
# 为用户创建token
token = md5(username=username)
# 存在就更新 不存在就创建
models.UserToken.objects.update_or_create(user=obj,defaults={"token":token})
ret["token"] = token
return JsonResponse(ret)
else:
ret["code"] =1002
ret["msg"] = "请求异常"
return JsonResponse(ret)
3.4 自定义认证类
自定义类模板
1 创建类 继承BasicAuthentication 自己实现authenticate方法(就是自定义实现认证的逻辑)
2 返回值:
--None,表示不管当前认证,让下一位类来进行认证
--raise exceptions.AuthenticationFailed("用户认证失败") # from rest_framework import exceptions
--(元素1,元素2) # 元素1赋值给request.user 元素二赋值给request.auth
3 局部使用或全局使用
自定义认证类案例
from rest_framework import exceptions from rest_framework.authentication import BasicAuthentication
# 第一步要写一个认证类 继承BasicAuthentication class Authtication(BasicAuthentication):
# 第二步重写authenticate方法 def authenticate(self,request): token = request._request.GET.get("token") token_obj = models.UserToken.objects.filter(token=token).first() if not token_obj: raise exceptions.AuthenticationFailed("用户认证失败") # 在restframwork内部会将两个字段赋值给request,以供后续操作 return (token_obj.user,token_obj) def authenticate_header(self, request): # 此方法带着 不必写代码逻辑 pass
使用自定义类
局部使用 views.py
from rest_framework import exceptions
from rest_framework.authentication import BasicAuthentication
# 第一步要写一个认证类
class Authtication(BasicAuthentication):
# 第二步重写authenticate方法
def authenticate(self,request):
# token = request._request.get("token")
token_obj = models.UserToken.objects.filter(token=token).first()
if not token_obj:
raise exceptions.AuthenticationFailed("用户认证失败")
# 在restframwork内部会将两个字段赋值给request,以供后续操作
return (token_obj.user,token_obj)
def authenticate_header(self, request):
pass
class OrderVIew(APIView):
'''
登陆之后可以查看订单信息
'''
# 第三步使用自定义类
authentication_classes = [Authtication,]
# 获取订单信息
# self.dispatch
def get(self,request,*args,**kwargs):
# 要首先判断是否是登录 登录的才能看订单信息
# request.user 对应class Authtication(object):中的return (token_obj.user,token_obj)第一个参数
# request.auth 对应class Authtication(object):中的return (token_obj.user,token_obj)第二个参数
ret = {"code":1000,"msg":None,"data":None}
try:
ret["data"] = order_dict
except Exception as e:
pass
return JsonResponse(ret)
全局使用
REST_FRAMEWORK = {
# 'DEFAULT_AUTHENTICATION_CLASSES': ['apps.api.utils.auth.Authtication',],
'DEFAULT_AUTHENTICATION_CLASSES': ['apps.api.utils.auth.Authtication'], # 是自定义认证类的路径
'UNAUTHENTICATED_USER': None,
'UNAUTHENTICATED_TOKEN': None,
}
或者
REST_FRAMEWORK = {
# python中认证的配置
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.BasicAuthentication', # 基本认证
'rest_framework.authentication.SessionAuthentication', # session认证
),
# python中权限的配置,如果没有指明,系统默认的权限是允许所有人访问的
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.AllowAny',
# 全局配置:一站式网站(所有操作都需要登录后才能访问)
# 'rest_framework.permissions.IsAuthenticated',
)
}
# 和认证组件类似 也是从dispatch开始往后看
self.check_permissions(request)
认证细则:
def check_permissions(self, request):
# 遍历权限对象列表得到一个个权限对象(权限器),进行权限认证
for permission in self.get_permissions():
# 权限类一定有一个has_permission权限方法,用来做权限认证的
# 参数:权限对象self、请求对象request、视图类对象
# 返回值:有权限返回True,无权限返回False
if not permission.has_permission(request, self):
self.permission_denied(
request, message=getattr(permission, 'message', None)
)
#权限组件自带的四个权限,默认全局配置的是AllowAny,
- 可以在settings.py中更换全局配置
- 可以在视图类中针对性的局部配置
- 可以自定义
class AllowAny(BasePermission):
# 游客与登陆用户都有所有权限
def has_permission(self, request, view):
return True
class IsAuthenticated(BasePermission):
# 游客没有任何权限,登陆用户才有权限
def has_permission(self, request, view):
return bool(request.user and request.user.is_authenticated)
class IsAdminUser(BasePermission):
# 游客没有任何权限,登陆用户才有权限
def has_permission(self, request, view):
return bool(request.user and request.user.is_staff)
class IsAuthenticatedOrReadOnly(BasePermission):
# 认证规则必须是只读请求或是合法用户: 游客只读,合法用户无限制
def has_permission(self, request, view):
return bool(
request.method in SAFE_METHODS or
request.user and
request.user.is_authenticated
)
''' 1) 创建继承BasePermission的权限类 2) 实现has_permission方法 3) 实现体根据权限规则 确定有无权限 4) 进行全局或局部配置 认证规则 i.满足设置的用户条件,代表有权限,返回True ii.不满足设置的用户条件,代表有权限,返回False '''
使用
局部使用
# 第一步 自定义权限类 继承BasePermission
from rest_framework.permissions import BasePermission
class MyPermission(BasePermission):
def has_permission(self,request,view):
if request.user.user_type !=3:
return False # 无权访问
return True # 有权访问
class OrderVIew(APIView):
# 添加一个需求 用户等级是3就可以访问
permission_classes = [MyPermission,] # 局部权限配置
def get(self,request,*args,**kwargs):
ret = {"code":1000,"msg":None,"data":None}
try:
ret["data"] = order_dict
except Exception as e:
pass
return JsonResponse(ret)
settings.py进行配置 如有视图中还有 permission_classes = [],优先 使用视图中 没有的再去配置文件中找
REST_FRAMEWORK = {
# DRF中认证的配置
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.BasicAuthentication', # 基本认证
'rest_framework.authentication.SessionAuthentication', # session认证
)
# DRF中权限的配置
# python中权限的配置,如果没有指明,系统默认的权限是允许所有人访问的
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.AllowAny', # 所有用户都应权限
# 一站式网站(所有操作都需要登录后才能访问)
# 'rest_framework.permissions.IsAuthenticated',
# 自定义配置的类
#‘DEFAULT_PERMISSION_CLASSES‘:[‘app01.utils.permission.SVIPPermission‘,]#可以自定义多个权限 可以写地址
# 自定义配置的类
# 'rest_framework.permissions.MyPermission',
)
}
1. 和认证的流程一样,进入initial(request)
2. 其中check_throttles(request)是节流的函数
3. allow_request()就是节流函数(要复写)(get_throttles循环所有节流类)
4. VisitThrottle自定义权限类
allow_request()返回值:
- True, 允许访问
- False, 访问太频繁
wait()返回值:返回一个整数,表示下次还有多久可以访问
1 继承BaseThrottle,自己实现allow_request(返回True或者False表示可以访问或者访问频率太高)
和wait方法 (表示还需等待到少秒)
2 继承SimpleRateThrottle,自己实现 get_cache_key和设置一个scope(配置文件字段)
from rest_framework.throttling import SimpleRateThrottle
class VisitThrottle(SimpleRateThrottle):
scope = "Vistor"
# 获取用户的IP
def get_cache_key(self, request, view):
return self.get_ident(request)
class UserThrottle(SimpleRateThrottle):
scope = "User"
def get_cache_key(self, request, view):
return self.user.username
局部配置 全局配置
class AuthView(APIView):
# 局部配置
throttle_classes = [VisitThrottle,]
全局配置
1 : 针对第一种继承类方法
'DEFAULT_THROTTLE_CLASSES': ['apps.api.utils.throttle.UserThrottle'], # 是自定义类的路径
2 : 针对第二种继承类方法
'DEFAULT_THROTTLE_RATES': {
'Vistor': '3/m',
'User': '10/m'
},
from rest_framework.throttling import SimpleRateThrottle
class VisitThrottle(SimpleRateThrottle):
scope = "Vistor"
def get_cache_key(self, request, view):
return self.get_ident(request)
class UserThrottle(SimpleRateThrottle):
scope = "User"
def get_cache_key(self, request, view):
return self.user.username
# 节流控制
from rest_framework.throttling import BaseThrottle
import time
# 设置一个空字典 放每次匿名登录的时间
# 有个缺点 将用户ip放到全局变量中 每次启动时都会清空
VISIT_REVORD={}
class VisitThrottle(BaseThrottle):
'''
根据IP来限制用户的访问次数
'''
def __init__(self):
self.history = None
def allow_request(self, request, view):
# 获取用户的ip
remote_addr = request.META.get('REMOTE_ADDR')
ctime = time.time()
if remote_addr not in VISIT_REVORD:
# 讲用户IP添加到字典中
VISIT_REVORD[remote_addr] = [ctime,]
return True
# 获取到用户ip
history = VISIT_REVORD.get(remote_addr)
self.history = history
# 判断是否有ip 且判断字典最后一位是否小于当前时间减去30秒时
while history and history[-1] < ctime-30:
# 如果是减去最后一位 pop默认减去最后一位
history.pop()
if len(history) <3:
# 当次数小于三时 将当前操作时间放进去
history.insert(0,ctime)
return True
def wait(self):
'''
告知用户还需要等多少时间
'''
ctime = time.time()
# history[-1]使用户最用一次访问时间
wait_time = 30 -(ctime-self.history[-1]) # 提示用户还有多少秒才能访问
return wait_time

浙公网安备 33010602011771号