5

django-rest-framework 基础三 认证、权限和频率 - Hans_Wang

 2 years ago
source link: https://www.cnblogs.com/hans-python/p/16251241.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

django-rest-framework 基础三 认证、权限和频率

登录接口: 登录成功只要给前端返回json格式字符串,这个字符串中带一个随机字符串(可以使用uuid生成)

登录接口步骤:

前端传入用户名和密码,然后去user表中查找,能找到说明用户和密码没问题,登录成功,然后在userToken表中存一条记录,说明登录过了,再返回前端一个json字符串

1.1 登录接口

models.py

from django.db import models

# Create your models here.

class User(models.Model):
    username = models.CharField(max_length=64)
    password = models.CharField(max_length=128)
    user_type = models.IntegerField(choices=((1, "超级管理员"),(2,"管理员"),(3,"普通用户")))

class UserToken(models.Model):
    user = models.OneToOneField(to=User,on_delete=models.CASCADE)
    userToken = models.CharField(max_length=64)


views.py

from django.shortcuts import render

# Create your views here.
from rest_framework.viewsets import GenericViewSet
from authenticated.models import User,UserToken
from rest_framework.response import Response
from rest_framework.decorators import action
import uuid

class UserView(GenericViewSet):

    @action(methods=['POST'], detail=False)
    def login(self,request):
        username = request.data.get("username")
        password = request.data.get("password")
        user = User.objects.filter(username=username, password=password).first()

        if not user:
            return  Response({"code":1001,"msg":"用户名或密码错误"})

        token = str(uuid.uuid4())  # 获取一个不重复的值,做唯一标识
        # userToken表中有就更新,没有就创建
        #UserToken.objects.update_or_create(user=user, defaults={'token': token})
        UserToken.objects.update_or_create(user=user, defaults={'userToken': token})
        # 返回信息,并带着token
        return Response({"code":1000,"msg":"登录成功",'userToken': token}) 

urls.py

from django.contrib import admin
from django.urls import path,include
from authenticated import views
from rest_framework import routers

router = routers.SimpleRouter()
router.register('user',views.UserView, "user")

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include(router.urls))
]
post请求: http://127.0.0.1:8000/user/login/

1.2 认证

有了登录接口,就可以实现认证,如果要调用别的接口必须要先登录才可以。

例如有个图书的表有五个接口,要访问图书的五个接口必须就登录。

1. 先写一个类,继承BaseAuthentication,并重写authenticate方法,在方法中校验是否登录,登录则返回两个值,没有则拋异常
from rest_framework.authentication import BaseAuthentication
class xxxx(BaseAuthentication):
	def authenticate(self, request):
        
2.  全局配置和局部配置
全局配置:
settings.py中
    REST_FRAMEWORK = {
    "DEFAULT_AUTHENTICATION_CLASSES":["authenticated.authentication.LoingAuth"]
}
局部配置:
在视图类中:
authentication_classes = [xxxx,]

禁止局部署配置:
authentication_classes = []


登录则返回两个值:
    request.user 当前登录的用户
    request.auth 为当前登录用户的token

models.py

from django.db import models

# Create your models here.

class User(models.Model):
    username = models.CharField(max_length=64)
    password = models.CharField(max_length=128)
    user_type = models.IntegerField(choices=((1, "超级管理员"),(2,"管理员"),(3,"普通用户")))

class UserToken(models.Model):
    user = models.OneToOneField(to=User,on_delete=models.CASCADE)
    userToken = models.CharField(max_length=64)


class Book(models.Model):
    name = models.CharField(max_length=64)
    price = models.DecimalField(max_digits=5, decimal_places=2)
    author = models.CharField(max_length=64)

认证功能,在app中新建authentication.py

from rest_framework.authentication import BaseAuthentication
from authenticated.models import  UserToken
from rest_framework.exceptions import AuthenticationFailed

class LoingAuth(BaseAuthentication):
    def authenticate(self, request):
        token = request.query_params.get('token')
        user_token = UserToken.objects.filter(userToken=token).first()
        if not user_token:
            raise AuthenticationFailed("请先登录")
        return user_token.user, token

views.py

from django.shortcuts import render

# Create your views here.
from rest_framework.viewsets import GenericViewSet
from authenticated.models import User,UserToken
from rest_framework.response import Response
from rest_framework.decorators import action
import uuid


from rest_framework.viewsets import ModelViewSet
from authenticated.models import Book
from authenticated.serializer import BookSerializer
from authenticated.authentication import LoingAuth

class UserView(GenericViewSet):

    @action(methods=['POST'], detail=False)
    def login(self,request):

        username = request.data.get("username")
        password = request.data.get("password")
        user = User.objects.filter(username=username, password=password).first()

        if not user:
            return  Response({"code":1001,"msg":"用户名或密码错误"})

        token = str(uuid.uuid4())
        # userToken表中有就更新,没有就创建
        #UserToken.objects.update_or_create(user=user, defaults={'token': token})
        UserToken.objects.update_or_create(user=user, defaults={'userToken': token})
        return Response({"code":1000,"msg":"登录成功",'token': token})



class BookView(ModelViewSet):
    # 只对BookView单独进行认证(局部配置)
    authentication_classes = [LoingAuth,]
    queryset = Book.objects.all()
    serializer_class = BookSerializer

路由urls.py

from django.contrib import admin
from django.urls import path,include
from authenticated import views
from rest_framework import routers

router = routers.SimpleRouter()
router.register('user',views.UserView, "user")
router.register('books', views.BookView,"books")

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include(router.urls))
]

全局配置settings.py

REST_FRAMEWORK = {
    "DEFAULT_AUTHENTICATION_CLASSES":["authenticated.authentication.LoingAuth"],
}
# authenticated.authentication.LoingAuth 为写的认证的类
# 全局配置后局部配置的就可以取消了。
# 但是全局配置后如果有些类不想让它有认证,比如登录接口,它不能有认证否则就死循环了。

# 全局配置后,单独取消某一个接口的认证: 
class UserView(ViewSet):
	authentication_classes = []  # 让它等于空就可以了。

所有的接口必须登录后才能访问(给每个视图加认证),登录成功后如果是普通用户则只可查看全部或单条数据。如果想要增删改必须是管理员或超级管理员。

演示可以把五个接口写成两个视图:

在应用目录下创建permission.py文件

permission.py

from rest_framework.permissions import BasePermission


class userPermission(BasePermission):
    def has_permission(self, request, view):
        user_type = request.user.user_type
        # (1, "超级管理员"),(2,"管理员"),(3,"普通用户"),如果小于3说明是管理或超管用户
        if user_type <3:
            return True
        else:
            return False

视图views.py

from rest_framework.viewsets import GenericViewSet
from authenticated.models import User, UserToken
from authenticated.models import Book
from authenticated.serializer import BookSerializer
from authenticated.authentication import LoingAuth
from rest_framework.mixins import CreateModelMixin, ListModelMixin, DestroyModelMixin, UpdateModelMixin, \
    RetrieveModelMixin


    #用户登录接口此处省略(见上面1.2认证)
    
    
# 查看全部和单条。只要登录了谁都可以访问
class BookView(GenericViewSet, ListModelMixin, RetrieveModelMixin):
    authentication_classes = [LoingAuth, ]
    queryset = Book.objects.all()
    serializer_class = BookSerializer


#  只有管理和超管用户可以 创建、修改、新增

from authenticated.permission import userPermission


class BookDetailView(GenericViewSet, CreateModelMixin, UpdateModelMixin, DestroyModelMixin):
    authentication_classes = [LoingAuth]
    permission_classes = [userPermission]
    queryset = Book.objects.all()
    serializer_class = BookSerializer

路由 urls.py:

from django.contrib import admin
from django.urls import path,include
from authenticated import views
from rest_framework import routers

router = routers.SimpleRouter()
router.register('user',views.UserView, "user")
router.register('books', views.BookView,"books")
router.register('bookdetail', views.BookDetailView,"bookdetail")

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include(router.urls))
]

全局配置settings.py

REST_FRAMEWORK = {
   "DEFAULT_PERMISSION_CLASSES":["authenticated.permission.userPermission"],
}
# authenticated.permission.userPermission 为写的权限的类
# 全局配置后局部配置的就可以取消了。

# 全局配置后,单独取消某一个接口的权限: 
class UserView(ViewSet):
	permission_classes = []  # 让它等于空就可以了。

普通用户访问的时候会报没有权限

image-20220405011610943

2.1 权限总结:

两个视图:

BookView:获取所有,获取单条

BookDetailView:删除,修改,新增
上面两个视图都需要登录:authentication_classes = [LoginAuth, ]

BookDetailView必须有权限才能,加了一个权限,permission_classes = [UserPermission, ]

编写权限步骤

第一步:写一个类,继承BasePermission,重写has_permission,判断如果有权限,返回True,如果没有权限,返回False
第二步:局部配置和全局配置
局部配置
		class BookDetailView(GenericViewSet, CreateModelMixin, DestroyModelMixin, UpdateModelMixin):
    		permission_classes = [UserPermission, ]
    
全局配置
    settings.py
  		REST_FRAMEWORK={
			"DEFAULT_PERMISSION_CLASSES":["authenticated.permission.userPermission",]
		}

限制访问的频率

在应用目录创建throttle.py文件用来限制频率

throttle.py

from rest_framework.throttling import SimpleRateThrottle

class IpThrottle(SimpleRateThrottle):
    scope = 'min_3'   # 在settings.py定义给哪个类限制的频率

    # get_cache_key 返回什么就以什么做限制,现在是以IP做限制
    def get_cache_key(self, request, view):
        return request.META.get('REMOTE_ADDR')  # 返回的是客户端的IP,以IP做限制
    	# return request.user.id # 返回已经登录的用户的id,以用户id做限制

views.py

from authenticated.throttle import IpThrottle

# 查看全部和单条。只要登录了谁都可以访问
class BookView(GenericViewSet, ListModelMixin, RetrieveModelMixin):
    authentication_classes = [LoingAuth, ]
    throttle_classes = [IpThrottle]  # 访问BookView类做限制
    queryset = Book.objects.all()
    serializer_class = BookSerializer

settings.py

REST_FRAMEWORK = {
    "DEFAULT_THROTTLE_RATES":{
        'min_3':'3/m',  
    },
}

# min_3 就是上面throttle.py.IpThrottle里scope定义的,这个一定要和scope定义的一致
# 3/m 每一分钟访问3次 ('s', 'sec', 'm', 'min', 'h', 'hour', 'd', 'day')
# 如果throttle.py.IpThrottle里还有别的限制的类,如果scope也是为min_3,那它也是每分钟访问3次的限制

超过3次就会报错:

image-20220405020938862

上面的配置为局部配置,也可以设置全局配置

settings.py

REST_FRAMEWORK = {
   "DEFAULT_THROTTLE_CLASSES" : ["authenticated.throttle.IpThrottle"], # 全局配置
    "DEFAULT_THROTTLE_RATES":{ 
        'min_3':'3/m',
    },
}
# 同样设置了全局,局部的throttle_classes = [IpThrottle]就可以不写了
# 如果只是某个类禁用:
throttle_classes = []

3.1 频率总结

第一步:写一个类,继承SimpleRateThrottle,重写类属性:scope,和get_cache_key方法
  	get_cache_key返回什么,就以什么做限制,
    scope配置文件中要用
    
第二步:在配置文件中配置
settings.py中
REST_FRAMEWORK = {
    "DEFAULT_THROTTLE_RATES":{
        'min_3':'3/m',  # minute_3是scope的字符串,一分钟访问3次
    },
}

第三步: 使用
1. 局部使用--》视图类中
  class BookView(GenericViewSet, ListModelMixin, RetrieveModelMixin):
    throttle_classes = [IPThrottle]

    
2. 全局使用--配置文件中
settings.py中
REST_FRAMEWORK = {
    "DEFAULT_THROTTLE_CLASSES" : ["authenticated.throttle.IpThrottle"],
    "DEFAULT_THROTTLE_RATES":{
        'min_3':'3/m',  # minute_3是scope的字符串,一分钟访问3次
    },
}

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK