restframework(6):权限组件
权限简介
权限说明
权限是针对某种对象的全局性的设置,而不是针对某种对象的某个特定实例的。例如,我们可以说“玛丽可以改写故事”,但是不能说“玛丽可以改写故事,但只限于她自己的故事”,也不能说“玛丽只能改写符合特定条件(如出版日期、编号)的故事”。后面的两种情况是 Django 的开发者正在讨论的东西,现在还不支持。
权限要求
目前,我们的API对谁可以编辑或删除代码段没有任何限制。我们希望有一些更高级的行为,以确保:
- 代码段始终与创建者相关联。
- 只有经过身份验证的用户才能创建摘要。
- 只有代码段的创建者可以更新或删除它。
- 未经身份验证的请求应具有完全只读访问权限。
restframework中的权限
权限即是通过继承BasePermission重构权限的类,权限的逻辑在类的has_permission方法中实现,返回True则有权限,返回False则没有权限。权限控制可以限制用户对于视图的访问和对于具体数据对象的访问。
- 在执行视图的dispatch()方法前,会先进行视图访问权限的判断
- 在通过get_object()获取具体对象时,会进行对象访问权限的判断
restframework提供权限
- AllowAny 允许所有用户
- IsAuthenticated 仅通过认证的用户
- IsAdminUser 仅管理员用户
- IsAuthenticatedOrReadOnly 认证的用户可以完全操作,否则只能get读取
权限简单实例
这里models还是上一篇博文中的那几张表,没有任何变动,我们需要注意的就是User类。
自定义权限类
这里的模型类我们不再解释了,在上一篇博文认证篇有详细的解释,这里的和上一篇的基本完全一致。
models.py:
class User(models.Model):
name=models.CharField(max_length=32)
pwd=models.CharField(max_length=32)
type_choices=((1,"普通用户"),(2,"VIP"),(3,"SVIP"))
user_type=models.IntegerField(choices=type_choices,default=1)
class SVIPPermission(object):
message="只有超级用户才能访问"
def has_permission(self,request,view):
username=request.user
user_type=User.objects.filter(name=username).first().user_type
if user_type==3:
return True # 通过权限认证
else:
return False
class MyPermission()
上面的自定义权限逻辑是判断我们的数据库中用户的等级机制问题,我们类比于以前的QQ,用户分为三级,普通,会员,超级会员。这里只是由于超级会员才让它通过权限,而在这之前,我们还可以介绍一下django中怎么直接用命令行创建用户。
用命令行创建用户
>>>python manage.py createuser
创建普通用户,部分特权。
>>>python manage.py createsuperuser
创建超级用户,在django中具有远超其它用户的最大的特权。
演示效果如下:
全局使用
我们可以在settings配置文件中增加配置:
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated',
)
}
而如果我们不写这个配置,那么系统会默认执行:
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.AllowAny',
)
局部使用
from rest_framework.permissions import IsAuthenticated
from rest_framework.views import APIView
class ExampleView(APIView):
permission_classes = (IsAuthenticated,)
...
自定义权限的使用
我们可以从前面的模块中获取到新的权限,我们可以依然延续前面的验证类中的模式,那么我们的LoginViews函数基本无变化:
class LoginView(APIView):
# authentication_classes = [TokenAuth,]
def post(self,request):
name=request.data.get("name")
pwd=request.data.get("pwd")
user=User.objects.filter(name=name,pwd=pwd).first()
res = {"state_code": 1000, "msg": None}
if user:
random_str=get_random_str(user.name)
token=Token.objects.update_or_create(user=user,defaults={"token":random_str})
res["token"]=random_str
else:
res["state_code"]=1001 #错误状态码
res["msg"] = "用户名或者密码错误"
import json
return Response(json.dumps(res,ensure_ascii=False))
我们可以从中获取到信息的取值,然后通过获取到的信息去判断案例信息,进一步得到我们的结果,利用postman测试如下:
如果该用户的类型不是3,则会显示:
权限源码解析
这里同样源码的步骤是三部分,和上一篇的认证是类似的,因为他们都在dispatch这个函数里面。
View -> dispatch函数
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(
# request,
# parsers=self.get_parsers(),
# authenticators=self.get_authenticators(),
# negotiator=self.get_content_negotiator(),
# parser_context=parser_context
# )
#request(原始request,[BasicAuthentications对象,])
#获取原生request,request._request
#获取认证类的对象,request.authticators
#1.封装request
request = self.initialize_request(request, *args, **kwargs)
self.request = request
self.headers = self.default_response_headers # deprecate?
try:
#2.认证
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
初始化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) # 频率功能
然后这里也没有什么能解释的,在上一篇都有详细的说法,我就不再复述了,那么我们来看权限函数里面到底有什么:
上述调用的两个函数
check_permissions函数
def check_permissions(self, request):
"""
Check if the request should be permitted.
Raises an appropriate exception if the request is not permitted.
"""
for permission in self.get_permissions():
if not permission.has_permission(request, self):
self.permission_denied(
request, message=getattr(permission, 'message', None)
)
函数的意思很明显,我们可以从它里面判断出获取到的信息值,如果是self.permission_denied的步骤那么我们可以省略,permission可以根据反射原理获取到值,那么我们继续来看剩下的三个函数,其中permission_denied函数本身是作为处理请求异常函数,我们这里不做多介绍,那么只介绍另外两个函数:
get_permissions()
def get_permissions(self):
"""
Instantiates and returns the list of permissions that this view requires.
"""
return [permission() for permission in self.permission_classes]
这个相当于向我们用户获取权限,如果我们用户没有赋值的话,那么就进入下一个函数里面,继续获取权限。加了一层循环遍历后得到结果。
permission_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
所以权限组件整体的源码比认证简单一些,因为认证组件还需要去找当前的user的静态方法在哪里,但权限基本就一个函数里面就调用完全了。
总结
认证组件和权限组件花了两天时间,整体上理解没什么难度,因为不管懂不懂源码,其实配置起来就一句话,而如果仔细去分析源码,一步步去抽丝剥茧,那么这两个的层数并不是很多,不像以前看django源码的时候,一次七八层,然后最后导致是有些懵,截止到目前,restframework的解析也写了六篇了,原计划是写十篇左右,下一篇看情况写下频率组件或者是路由吧,但其实频率的源码步骤是和认证、权限一样的,如果有时间,我会将其再补齐一下的。