drf认证功能,drf权限功能,认证功能源码分析

一.上节回顾

1 web应用开发模式
2 API接口
3 接口测试工具
4 restful规范(重点)
5 djangorestframework:drf,django的app,快速的写出符合restful规范的API接口
6 drf的执行流程(APIView源码:request对象,认证,权限,频率,捕获全局异常,响应对象)
7 序列化器(序列化,反序列化,数据校验):(重点*****)
-Serializer
	-字段自己写,字段类型,字段属性
	-read_only write_only
	-序列化:实例化得到对象:instance,many,data(many=True:__call__)
	-对象.data:字典
	-反序列化
	-重写:create,update
	-反序列化的校验通过,修改会触发update,新增会触发create
	-反序列化的数据校验(三种方式):源码部分
	-扩展:source,SerializerMethodField
-ModelSerializer
	-内部类
		-class Meta:
			model=model.Book
			fileds='__all__'
			exclude=[]
			read_only_field=[]
			extra_kwargs={}
			depth=1
	-Serializer用法,重写字段,指定字段类型,字段参数
	-局部钩子,全局钩子
	-不需要重写create和update(经常看到重写)
8 请求和响应(次重点)
-Request
	-request.data
	-request.query_param
	-读源码:重写了__getattr__
-Response
	-客户端请求数据看到的样子(json,浏览器)
	-局部配置:在视图函数中render_classes=[]
	-全局配置:settings中配置
		-drf默认有一套配置文件
9 视图(重点)
-2个视图基类:APIView和GenericAPIView
	-GenericAPIView:
		-queryset=None
		-serializer_class=None
		-get_queryset:获取要序列化的数据(可以重写,自己定制规则)
		-get_serializer:获取序列化类(可以重写,自己定制返回哪个序列化类)
		-get_object:返回单个对象,通过pk查的(可以重写,返回谁,单个对象就是谁)
-5个视图扩展类(rest_framework.mixin)
	CreateModelMixin:create方法创建一条
	DestroyModelMixin:destroy方法删除一条
	ListModelMixin:list方法获取所有
	RetrieveModelMixin:retrieve获取一条
	UpdateModelMixin:update修改一条
-9个视图扩展类
	CreateAPIView:继承CreateModelMixix,GenericAPIView,有post方法,新增数据
	DestroyAPIView:继承DestroyModelMixin,GenericAPIView,有delete方法,删除数据
	ListAPIView:继承ListModelMixin,GenericAPIView,有get方法获取所有
	UpdateAPIView:继承UpdateModelMixin,GenericAPIView,有put和patch方法,修改数据
    RetrieveAPIView:继承RetrieveModelMixin,GenericAPIView,有get方法,获取一条

    ListCreateAPIView:继承ListModelMixin,CreateModelMixin,GenericAPIView,有get获取所有,post方法新增
    RetrieveDestroyAPIView:继承RetrieveModelMixin,DestroyModelMixin,GenericAPIView,有get方法获取一条,delete方法删除
     RetrieveUpdateAPIView:继承RetrieveModelMixin,UpdateModelMixin,GenericAPIView,有get获取一条,put,patch修改
     RetrieveUpdateDestroyAPIView:继承RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin,GenericAPIView,有get获取一条,put,patch修改,delete删除
	-视图集
		ViewSetMixin:重写了as_view(一旦继承了它,路由就变了)
		ViewSet:继承ViewSetMixin和APIView
		GenericViewSet:继承ViewSetMixin,generic.GenericAPIView
		ModelViewSet:继承mixins.CreateModelMixin,mixins.RetrieveModelMixin,mixins.UpdateModelMixin,mixins.DestroyModelMixin,mixins.ListModelMixin,GenericViewSet
        ReadOnlyModelViewSet:继承mixins.RetrieveModelMixin,mixins.ListModelMixin,GenericViewSet
10 路由(重点)
-三种方式配置路由
	-自动生成路由
		-导入SimpleRouter,实例化得到对象
		-router.register('book',视图类(必须继承ViewSetMixin及它的子类))
		-两种方式注册进路由中
			-urlpatterns += router.urls
			-urlpatterns=[
				...
				url(r'^',include(router.urls))
				]
		-半自动
			url(r'books/',BookView.as_view({'get':'list'}))
		-原始的
	-action装饰器(也会自动生成路由)
		-127.0.0.1/books/get_new_5 detail=False
		-127.0.0.1/books/pk/get_new_5 detail=True
		-@action(method=['put'],detail=True)
		def get_new_5(self,request,pk):
			return Response({'msg':'获取5条数据成功'})

二.今日内容

1 drf认证功能介绍

0 认证,频率,权限
1 用户是否登录到系统中
2 后期基本上会用JWT的认证
3 自定制的认证

2 认证功能源码分析

1 APIView--->dispatch--->self.initial(request,*args,**kwargs)-->self.perform_authentication(request)--->Request.user--->self._authenticate(self):Request类的方法--->self.authenticators:Request类的属性--->在Request对象实例化的时候传入的--->Request在什么时候实例化的?dispatch的时候--->APIView:self.get_authenticators()-->return[auth() for auth in self.authentication_classes]--->如果在自己定义的视图类中写了authentication_classes=[1,2]--->Request的self.authenticators就编程了我们配置的一个个类的对象

2 self._authenticate(self):Request类的方法
def _authenticate(self):
	for authenticator in self.authenticators:  # BookView中配置的一个个类的对象
		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
3 只要在视图类中配置authentication_classes=[MyAuthen.LoginAuth,]就会执行上面的方法,执行认证

3.自定义认证类(重点)

1 使用
	-在app下建一个任意名字的py文件
	-定义一个类,继承BaseAuthentication
	from rest_framework.authentication import BaseAuthentication
	class LoginAuth(BaseAuthentication):
		def authenticate(self,request):
			token=request.GET.get('token')
			res=models.UserInfo.objects.filter(token=token).first()
			if res:
				return 元组
			else:
				raise AuthenticationFailed('您没有登录')
	-重写authenticate方法
	-局部使用和全局使用
		-局部:在视图类中配置(只要配置了,就要登录以后才能访问,没配置,不用登录就能访问)
			authentication_classes=[myauthen.LoginAuth,]# 文件名.类名
		-全局
		REST_FRAMEWORK={
	"DEFAULT_AUTHENTICATION_CLASSES":["app01.myauthen.LoginAuth",]
	}

	-注意:
	1.认证类,认证通过可以返回一个元祖,有两个值,第一个值会给request.user,第二个值会给request.auth
	2.认证类可以配置多个,按照从前向后的顺序执行,如果前面有返回值,认证就不再继续往下走了

4.认证功能局部使用和全局使用

1 全局使用(所有接口,都需要登录才能访问)
	-在配置文件中
	 REST_FRAMEWORK = {
        "DEFAULT_AUTHENTICATION_CLASSES": ["app01.myauthen.LoginAuth", ]
        }
2 局部使用
	-在想局部使用的视图类上写上下面这条
	authentication_classes = [myauthen.LoginAuth,]

3 局部禁用
	-在想禁用的视图类上
	authentication_classes = []
		

代码演示

### urls.py
urlpatterns = [
    path('admin/', admin.site.urls),
    path('login/', views.LoginView.as_view()),
    path('books/', views.BookView.as_view()),
    path('publish/', views.PublishView.as_view()),
]

### models.py
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)

### myauthen.py
from app01 import models
from rest_framework.exceptions import AuthenticationFailed

from rest_framework.authentication import BaseAuthentication


class LoginAuth(BaseAuthentication):
    def authenticate(self, request):
        # 写认证规则,如果过认证通过,就正常return
        # 如果认证失败,抛异常
        # 取出携带的随机字符串,去数据判断,如果有正常登录,没有,告诉它没有登录
        # 在get请求的参数中,放到请求头中
        token = request.GET.get('token')

        res = models.UserToken.objects.filter(token=token).first()
        if res:
            return (res.user, token)
        else:
            raise AuthenticationFailed('您没有登录')
### settings.py
 # 全局使用认证规则
REST_FRAMEWORK = {
    "DEFAULT_AUTHENTICATION_CLASSES": ["app01.MyAuthen.LoginAuth", ],
}         
### views.py
from app01 import MyAuthen
from rest_framework.views import APIView
from rest_framework.request import Request
from rest_framework.response import Response


class BookView(APIView):
    authentication_classes = [myauthen.LoginAuth,]
    def get(self, request):
        print(request.user) # 当前登录用户
        print(request.auth) # 我们给了token
        return Response('获取了所有图书')

class PublishView(APIView):

    def get(self, request):
        return Response('获取了出版社')

from app01 import models
import uuid


class LoginView(APIView):
    authentication_classes = []
    def post(self, request):
        res = {'code': 100, 'msg': '登录成功'}
        username = request.data.get('username')
        password = request.data.get('password')
        # 查询数据库
        user = models.User.objects.filter(username=username, password=password).first()

        if user:
            token = uuid.uuid4()  # 生成一个随机字符串
            # 把token存到UserToken表中(如果是第一次登录:新增,如果不是第一次:更新)
            models.UserToken.objects.update_or_create(defaults={'token': token}, user=user)
            res['token'] = token
            return Response(res)
        else:
            res['code'] = 101
            res['msg'] = '用户名或密码错误'

            return Response(res)

5 自定义权限功能(重点)

1 登录成功以后,超级用户可以干某些事,普通用户不能干-->超级用户可以查看某些接口,普通用户不能查看

2 使用
-写一个类继承BasePermission,重写has_permission
	class SuperPermission(BasePermission):
        def has_permission(self, request, view):
            # Return `True` if permission is granted, `False` otherwise.
            # 超级用户可以访问,除了超级用户以外,都不能访问
            if request.user.user_type == 1:
                return True
            else:
                return False
3 局部使用和全局使用
-在想局部使用的视图类上
	permission_classes = [myauthen.SuperPermission]
    -全局使用
      REST_FRAMEWORK = {
        "DEFAULT_PERMISSION_CLASSES": ["app01.myauthen.SuperPermission", ]
        }
     -局部禁用
    permission_classes = []

6 权限功能局部使用和全局使用

1 使用方式
    -在想局部使用的视图类上
	permission_classes = [myauthen.SuperPermission]
    -全局使用
      REST_FRAMEWORK = {
        "DEFAULT_PERMISSION_CLASSES": ["app01.myauthen.SuperPermission", ]
        }
     -局部禁用
    permission_classes = []

代码演示

### urls.py
urlpatterns = [
    path('admin/', admin.site.urls),
    path('login/', views.LoginView.as_view()),
    path('books/', views.BookView.as_view()),
    path('publish/', views.PublishView.as_view()),


]

### models.py
# 用户表

class User(models.Model):
	username=models.CharField(max_length=32)
	password=models.CharField(max_length=32)

	user_type=models.IntegerField(choices=((1,'超级用户'),(2,'普通用户'),(3,'沙雕用户')),default=1)

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

### settings.py
# 全局使用认证规则
REST_FRAMEWORK = {
    "DEFAULT_AUTHENTICATION_CLASSES": ["app01.myauthen.LoginAuth",],
    "DEFAULT_PERMISSION_CLASSES": ["app01.myauthen.SuperPermission", ]
}

### myauthen.py
from app01 import models
from rest_framework.exceptions import AuthenticationFailed

from rest_framework.authentication import BaseAuthentication


class LoginAuth(BaseAuthentication):
	def authenticate(self, request):

		# 写认证规则,如果认证通过,就正常return
		# 如果认证失败,抛异常
		# 取出携带的随机字符串,去数据判断,如果有正常登录,没有,告诉它没有登录
		# 在get请求的参数中,放到请求头中
		token = request.GET.get('token')
		res = models.UserToken.objects.filter(token=token).first()
		if res:
			return (res.user, token)
		else:
			raise AuthenticationFailed('您没有登录')


from rest_framework.permissions import BasePermission


class SuperPermission(BasePermission):
	def has_permission(self, request, view):
		# 超级用户可以访问,其他用户都不能访问
		user=request.user
		if user.user_type == 1:
			return True
		else:
			return False

### views.py
from app01 import myauthen
from app01 import models
from rest_framework.response import Response
from rest_framework.views import APIView
class BookView(APIView):
	# 局部使用认证功能,及权限功能
	authentication_classes = [myauthen.LoginAuth,]
	permission_classes = [myauthen.SuperPermission,]
	def get(self,request):
		print(request.user)
		print(request.auth)
		return Response('获取了所有图书')

class PublishView(APIView):

	def get(self,request):
		return Response('获取了所有出版社')

import uuid
class LoginView(APIView):
	# 局部使用认证功能
	authentication_classes = [myauthen.LoginAuth,]
	def post(self,request):
		res={'code':100,'msg':'登录成功'}
		username=request.data.get('username')
		password=request.data.get('password')
		# 查数据库
		user=models.User.objects.filter(username=username,password=password).first()
		if user:
			token=uuid.uuid4()  # 生成一个随机字符串
			# token存到UserToken表(首次登录:新增,非首次登录,更新)
			models.UserToken.objects.update_or_create(defaults={'token':token},user=user)

			res['token']=token
			return Response(res)
		else:
			res['code']=101
			res['msg']='用户名或密码错误'
			return Response(res)

7 内置的权限和认证类

# 内置认证类
from rest_framework.exceptions import AuthenticationFailed
# 内置权限类
from rest_framework.permissions import BasePermission

扩展

1 select_related的使用
articleList=models.Article.objects.select_related("category").all()
for article_obj in articleList:
        #  Doesn't hit the database, because article_obj.category
        #  has been prepopulated in the previous query.
        #不再查询数据库,因为第一次查询,数据已经填充进去了
        print(article_obj.category.title)
        
2 mysql的悲观锁和乐观锁

3 drf内部内置了一些认证类,分别干了什么事

三.作业

0 整理出认证和权限的使用,局部配置和全局配置
1 写一个图书的5个接口出版社的5个接口和登录接口
	-用户必须登录才能访问图书的5个接口
	-必须超级用户登录后才能访问出版社5个接口
2 阅读认证源码,整理出流程
2(拓展)阅读权限源码,整理出流程(错误信息的中文显示)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值