python之路_rest-framework之认证、权限、频率
一、认证
1、自定义认证
认证即是通过继承BaseAuthentication重构认证的类,认证的逻辑在类的authenticate方法中实现,通过判断用户传递的token,如果认证成功返回(用户,用户Token)元组,会将用户对象封装到request里,通过request.用户可以获得用户的相关信息。具体代码如下:
from rest_framework.authentication import BaseAuthentication from rest_framework import exceptions token_list = [ 'sfsfss123kuf3j123', 'asijnfowerkkf9812', ] class TestAuthentication(BaseAuthentication): def authenticate(self, request): """ 用户认证,如果验证成功后返回元组: (用户,用户Token) :param request: :return: None,表示跳过该验证; 如果跳过了所有认证,默认用户和Token和使用配置文件进行设置 self._authenticator = None if api_settings.UNAUTHENTICATED_USER: self.user = api_settings.UNAUTHENTICATED_USER() else: self.user = None if api_settings.UNAUTHENTICATED_TOKEN: self.auth = api_settings.UNAUTHENTICATED_TOKEN() else: self.auth = None (user,token)表示验证通过并设置用户名和Token; AuthenticationFailed异常 """ val = request.query_params.get('token') if val not in token_list: raise exceptions.AuthenticationFailed("用户认证失败") return ('登录用户', '用户token') def authenticate_header(self, request): """ Return a string to be used as the value of the `WWW-Authenticate` header in a `401 Unauthenticated` response, or `None` if the authentication scheme should return `403 Permission Denied` responses. """ # 验证失败时,返回的响应头WWW-Authenticate对应的值 pass
2、局部认证
上述为我们的重构的一个认证的实例。但是怎么才能用到我们需要进行认证的视图上呢?通过在具体视图中配置此认证类即可,具体实例如下:
方式一:
from rest_framework.views import APIView from rest_framework.response import Response class TestView(APIView): authentication_classes = [TestAuthentication, ] #局部认证配置 def get(self, request, *args, **kwargs): print(request.user) print(request.auth) return Response('GET请求,响应内容') def post(self, request, *args, **kwargs): return Response('POST请求,响应内容') def put(self, request, *args, **kwargs): return Response('PUT请求,响应内容')
方式二:
class MyViewAuth(object): authentication_classes = [TestAuthentication, ] from rest_framework.views import APIView from rest_framework.response import Response class TestView(MyViewAuth,APIView): #视图继承自定义MyViewAuth类,必须写在左边 def get(self, request, *args, **kwargs): print(request.user) print(request.auth) return Response('GET请求,响应内容') def post(self, request, *args, **kwargs): return Response('POST请求,响应内容') def put(self, request, *args, **kwargs): return Response('PUT请求,响应内容')
3、全局认证
上述我们介绍了局部视图中配置认证的方式。但是如果我们需要对全局所有的视图进行认证,则需要在配置文件中进行配置来实现了。具体配置如下:
REST_FRAMEWORK = { 'UNAUTHENTICATED_USER': None, 'UNAUTHENTICATED_TOKEN': None, "DEFAULT_AUTHENTICATION_CLASSES": [ "app01.utils.TestAuthentication", #TestAuthentication文件中写自定义认证类 ], }
二、权限
1、自定义权限
权限即是通过继承BasePermission重构权限的类,权限的逻辑在类的has_permission方法中实现,返回True则有权限,返回False则没有权限,具体示例如下:
from rest_framework.permissions import BasePermission class TestPermission(BasePermission): message = "权限验证失败" def has_permission(self, request, view): """ 判断是否有权限访问当前请求 Return `True` if permission is granted, `False` otherwise. :param request: :param view: :return: True有权限;False无权限 """ if request.user == "管理员": return True # GenericAPIView中get_object时调用 def has_object_permission(self, request, view, obj): """ 视图继承GenericAPIView,并在其中使用get_object时获取对象时,触发单独对象权限验证 Return `True` if permission is granted, `False` otherwise. :param request: :param view: :param obj: :return: True有权限;False无权限 """ if request.user == "管理员": return True
2、局部使用
class TestView(APIView): # 认证的动作是由request.user触发 authentication_classes = [TestAuthentication, ] # 权限 # 循环执行所有的权限 permission_classes = [TestPermission, ] def get(self, request, *args, **kwargs): # self.dispatch print(request.user) print(request.auth) return Response('GET请求,响应内容') def post(self, request, *args, **kwargs): return Response('POST请求,响应内容') def put(self, request, *args, **kwargs): return Response('PUT请求,响应内容')
3、全局使用
REST_FRAMEWORK = { 'UNAUTHENTICATED_USER': None, 'UNAUTHENTICATED_TOKEN': None, "DEFAULT_AUTHENTICATION_CLASSES": [ "app01.utils.TestAuthentication", ], "DEFAULT_PERMISSION_CLASSES": [ "app01.utils.TestPermission", ], }
三、频率
具体实现方式见如下应用中的实例。全局使用限流的配置的如下:
REST_FRAMEWORK = { "DEFAULT_THROTTLE_CLASSES":[ ], #限流类的路径,参考认证和权限 'DEFAULT_THROTTLE_RATES':{ 'wdp_anon':'5/minute', 'wdp_user':'10/minute', } }
四、应用实例
有这么个应用要求,主页内容通过认证,根据认证结果登录用户可以在一分钟之内访问10次,匿名用户一分钟只能访问5次。对于订单页面通过认证、权限、设置访问频率,要求只有登录用户才能访问,同样一分钟内只能访问10次。
1、视图代码
from django.shortcuts import render,HttpResponse from django.http import JsonResponse from rest_framework import views from .models import * from .utils.auth import LuffyAuthentication from .utils.permission import LuffyPermission from .utils.throttle import LuffyAnonRateThrottle,LuffyUserRateThrottle ############以下是登录认证相关视图########### def gen_token(username): import time import hashlib ctime=str(time.time()) hashcode=hashlib.md5(username.encode('utf-8')) hashcode.update(ctime.encode('utf-8')) return hashcode.hexdigest() class AuthView(views.APIView): def post(self,request,*args,**kwargs): ret={"code":1000,"msg":None} user=request.data.get("username") pwd=request.data.get("password") userobj=UserInfo.objects.filter(user=user,pwd=pwd).first() if userobj: tk=gen_token(user) Token.objects.update_or_create(user=userobj,defaults={'token':tk}) ret["code"]=1001 ret["token"]=tk else: ret["msg"]="用户名或者密码错误" return JsonResponse(ret) ############以下是首页视图########### class IndexView(views.APIView): """ 用户认证 http://127.0.0.1:8001/v1/index/?tk=sdfasdfasdfasdfasdfasdf 获取用户传入的Token 首页限制:request.user 匿名:5/m 用户:10/m """ authentication_classes = [LuffyAuthentication,] throttle_classes = [LuffyAnonRateThrottle,LuffyUserRateThrottle] def get(self,request,*args,**kwargs): return HttpResponse("首页") ############以下是订单页面视图########### class OrderView(views.APIView): """ 订单页面:只有登录成功后才能访问 - 认证(匿名和用户) - 权限(用户) - 限制() """ authentication_classes = [LuffyAuthentication,] permission_classes = [LuffyPermission,] throttle_classes = [LuffyUserRateThrottle,] def get(self, request, *args, **kwargs): return HttpResponse("订单页")
2、url代码
from django.conf.urls import url from django.contrib import admin from app01 import views urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^auth/', views.AuthView.as_view()), url(r'^index/', views.IndexView.as_view()), url(r'^order/', views.OrderView.as_view()), ]
3、认证代码
from rest_framework.authentication import BaseAuthentication from rest_framework import exceptions from app01 import models class LuffyAuthentication(BaseAuthentication): def authenticate(self, request): tk=request.query_params.get('tk') #拿取url上的token参数 if not tk: return (None,None) # raise exceptions.AuthenticationFailed("认证失败") #不允许有匿名用户情况 tokenobj=models.Token.objects.filter(token=tk).first() if not tokenobj: return (None,None) return (tokenobj.user,tokenobj)
4、权限代码
from rest_framework.permissions import BasePermission class LuffyPermission(BasePermission): def has_permission(self, request, view): """ Return `True` if permission is granted, `False` otherwise. """ if request.user: return True return False
5、限频代码
from rest_framework.throttling import SimpleRateThrottle class LuffyAnonRateThrottle(SimpleRateThrottle): '''匿名用户以ip为唯一标识''' scope = "luffy_anon" def allow_request(self, request, view): """ Return `True` if the request should be allowed, `False` otherwise. """ if request.user: return True # 获取当前访问用户的唯一标识 self.key = self.get_cache_key(request, view) # 根据当前用户的唯一标识,获取所有访问记录 # [1511312683.7824545, 1511312682.7824545, 1511312681.7824545] self.history = self.cache.get(self.key, []) # 获取当前时间 self.now = self.timer() # Drop any requests from the history which have now passed the # throttle duration while self.history and self.history[-1] <= self.now - self.duration: self.history.pop() if len(self.history) >= self.num_requests: return self.throttle_failure() return self.throttle_success() def get_cache_key(self, request, view): return 'throttle_%(scope)s_%(ident)s' % { 'scope': self.scope, 'ident': self.get_ident(request) } class LuffyUserRateThrottle(SimpleRateThrottle): '''登录用户以用户名为唯一标识''' scope = "luffy_user" def allow_request(self, request, view): """ Return `True` if the request should be allowed, `False` otherwise. """ if not request.user: return True # 获取当前访问用户的唯一标识 # 用户对所有页面 self.key = request.user.user # 用户对单页面 # self.key = request.user.user + view.__class__.__name__ # 根据当前用户的唯一标识,获取所有访问记录 # [1511312683.7824545, 1511312682.7824545, 1511312681.7824545] self.history = self.cache.get(self.key, []) # 获取当前时间 self.now = self.timer() # Drop any requests from the history which have now passed the # throttle duration while self.history and self.history[-1] <= self.now - self.duration: self.history.pop() if len(self.history) >= self.num_requests: return self.throttle_failure() return self.throttle_success()
6、限频配置
REST_FRAMEWORK = { 'UNAUTHENTICATED_USER': None, 'UNAUTHENTICATED_TOKEN': None, "DEFAULT_THROTTLE_RATES": { 'luffy_anon': '5/m', #5次每分钟 'luffy_user': '10/m' #10次每分钟 }, }