内置权限类分析
BasePermission
默认实现has_permission, has_object_permission方法
class BasePermission(metaclass=BasePermissionMetaclass):
"""
A base class from which all permission classes should inherit.
"""
def has_permission(self, request, view):
"""
Return `True` if permission is granted, `False` otherwise.
"""
return True
def has_object_permission(self, request, view, obj):
"""
Return `True` if permission is granted, `False` otherwise.
"""
return True
基本认证类等于没写一样,全返回True
AllowAny
class AllowAny(BasePermission):
"""
Allow any access.
This isn't strictly required, since you could use an empty
permission_classes list, but it's useful because it makes the intention
more explicit.
"""
def has_permission(self, request, view):
return True
继承父类BasePermission,这里没什么可说的,允许全部权限
IsAuthenticated
class IsAuthenticated(BasePermission):
"""
Allows access only to authenticated users.
"""
def has_permission(self, request, view):
return bool(request.user and request.user.is_authenticated)
继承父类BasePermission,使用bool判断,只允许认证过的人返回True,不然Flase
IsAdminUser
class IsAdminUser(BasePermission):
"""
Allows access only to admin users.
"""
def has_permission(self, request, view):
return bool(request.user and request.user.is_staff)
继承父类BasePermission,使用bool判断,意思是表示认证且用户属性is_staff为True的人才可以登录,也就是表示后台管理员可以登录
IsAuthenticatedOrReadOnly
class IsAuthenticatedOrReadOnly(BasePermission):
"""
The request is authenticated as a user, or is a read-only request.
"""
def has_permission(self, request, view):
return bool(
request.method in SAFE_METHODS or
request.user and
request.user.is_authenticated
)
继承父类BasePermission,使用bool判断,意思是 认证用户有权限,否则只能有读权限
DjangoModelPermissions
django 与内置模块一起的请求权限,视图中包括增删改查。这些我们可以调配
perms_map = {
'GET': [],
'OPTIONS': [],
'HEAD': [],
'POST': ['%(app_label)s.add_%(model_name)s'],
'PUT': ['%(app_label)s.change_%(model_name)s'],
'PATCH': ['%(app_label)s.change_%(model_name)s'],
'DELETE': ['%(app_label)s.delete_%(model_name)s'],
}
这里我们点击DjangoModelPermissions看详情使用
使用方法 settings
REST_FRAMEWORK = {
······
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.DjangoModelPermissions',
),
}
class DjangoModelPermissions(BasePermission):
"""
The request is authenticated using `django.contrib.auth` permissions.
See: https://docs.djangoproject.com/en/dev/topics/auth/#permissions
It ensures that the user is authenticated, and has the appropriate
`add`/`change`/`delete` permissions on the model.
This permission can only be applied against view classes that
provide a `.queryset` attribute.
"""
# Map methods into required permission codes.
# Override this if you need to also provide 'view' permissions,
# or if you want to provide custom permission codes.
perms_map = {
'GET': [],
'OPTIONS': [],
'HEAD': [],
'POST': ['%(app_label)s.add_%(model_name)s'],
'PUT': ['%(app_label)s.change_%(model_name)s'],
'PATCH': ['%(app_label)s.change_%(model_name)s'],
'DELETE': ['%(app_label)s.delete_%(model_name)s'],
}
authenticated_users_only = True
def get_required_permissions(self, method, model_cls):
"""
Given a model and an HTTP method, return the list of permission
codes that the user is required to have.
"""
kwargs = {
'app_label': model_cls._meta.app_label,
'model_name': model_cls._meta.model_name
}
if method not in self.perms_map:
raise exceptions.MethodNotAllowed(method)
return [perm % kwargs for perm in self.perms_map[method]]
def _queryset(self, view):
assert hasattr(view, 'get_queryset') \
or getattr(view, 'queryset', None) is not None, (
'Cannot apply {} on a view that does not set '
'`.queryset` or have a `.get_queryset()` method.'
).format(self.__class__.__name__)
if hasattr(view, 'get_queryset'):
queryset = view.get_queryset()
assert queryset is not None, (
'{}.get_queryset() returned None'.format(view.__class__.__name__)
)
return queryset
return view.queryset
def has_permission(self, request, view):
# Workaround to ensure DjangoModelPermissions are not applied
# to the root view when using DefaultRouter.
if getattr(view, '_ignore_model_permissions', False):
return True
if not request.user or (
not request.user.is_authenticated and self.authenticated_users_only):
return False
queryset = self._queryset(view)
perms = self.get_required_permissions(request.method, queryset.model)
return request.user.has_perms(perms)
优点: 不用在每个视图里去写了,
缺点: 如果你一但写一个viewset没有queryset的话,将会出错,解决:使用permission_classes = (IsAuthenticated, )
写在你的viewset里,自己定义其他认证方式,覆盖全局的配置即可。
自己动手写一个权限类实现效果
views.py
class MenuViewSet(ModelViewSet, TreeAPIView):
'''
菜单管理:增删改查
权限树
list:
获取菜单+id具体菜单
create:
添加一个菜单
delete:
删除一个菜单
update:
修改一个菜单
'''
perms_map = ({'*': 'admin'}, {'*': 'menu_all'}, {'get': 'menu_list'}, {'post': 'menu_create'}, {'put': 'menu_edit'},
{'delete': 'menu_delete'})
queryset = Menu.objects.all()
serializer_class = MenuSerializer
pagination_class = CommonPagination
filter_backends = (SearchFilter, OrderingFilter,)
search_fields = ('name',)
ordering_fields = ('sort',)
# authentication_classes = (JSONWebTokenAuthentication, SessionAuthentication,)
authentication_classes = (SessionAuthentication,)
permission_classes = (RbacPermission,)
custom.py
class RbacPermission(BasePermission):
'''
自定义权限
'''
@classmethod
def get_permission_from_role(self, request):
try:
perms = request.user.roles.values(
'permissions__method',
).distinct()
return [p['permissions__method'] for p in perms]
except AttributeError:
return None
def has_permission(self, request, view):
perms = self.get_permission_from_role(request)
if perms:
if 'admin' in perms:
return True
elif not hasattr(view, 'perms_map'):
return True
else:
perms_map = view.perms_map
_method = request._request.method.lower()
for i in perms_map:
for method, alias in i.items():
if (_method == method or method == '*') and alias in perms:
return True
这个认证类查看视图类定义的功能权限是否有与数据库中的权限,比如删除,导出等,这些都是功能权限。如果有就返回True,没有就Flase。实现完功能权限就ok了吗。在一个小型平台上面数据还没做到数据的权限不同。
上面仅仅实现了功能权限的功能,并没有实现数据权限功能,比如公司有个平台实现多学校缴费的功能,
那么每个学校的数据互相是不能看的,那么这个时候数据权限就体现出重要性了。可以怎么实现呢。
https://segmentfault.com/a/1190000004400312