Связанные концепции
- ACL
ACL — это аббревиатура от списка управления доступом, который называется списком управления доступом и содержит определение разрешений на то, какие операции могут выполняться над объектом или записью.
Например, ACL файлового объекта: { "Алиса": { "чтение": истина, "запись": истина}, "Боб": { "чтение": истина } },
Это означает, что Алиса может и читать, и писать в файл, а Боб может только читать.
- RBAC
Управление доступом на основе ролей RBAC (управление доступом на основе ролей) отличается от предоставления разрешений пользователям, но назначением разрешений ролям.
В модели RBAC «разрешения» соответствуют только «ролям», а пользователи также соответствуют «ролям», назначают роли пользователям, а затем управляют разрешениями ролей, завершая разделение разрешений и пользователей.
- RBAC0/RBAC1/RBAC2/RBAC3
Основные особенности RBAC0:
Существует ли отношение «многие к одному» или «многие ко многим» между пользователями и ролями.
Основные возможности RBAC1:
Роли могут наследоваться, образуя дерево.
Основные особенности RBAC2:
Роли могут быть взаимоисключающими. (касса и бухгалтерия) Кардинальные ограничения. (Исполнительный директор) предпосылки. (обновление слой за слоем) Взаимное исключение во время выполнения. (Во время выполнения разрешена только одна роль — три состояния воды)
RBAC3 объединяет rabac1 и rbac2.
- разрешение на данные
object (row) level permissions
model (table) level permissions
- Интуитивное представление разрешений
Разрешения на эксплуатацию: Меню и кнопки для страниц веб-системы Привилегия данных: операция регистрации данных в веб-системе
разрешения django по умолчанию
Django поставляется с простой системой разрешений. Он предоставляет методы для назначения разрешений указанным пользователям и группам пользователей.
Группа и роль здесь на самом деле являются концепцией
Объект User имеет два поля «многие ко многим»: groups и user_permissions. Доступ к пользовательским объектам можно получить, как и к другимDjango model
: так же, как доступ к их связанным объектам.
myuser.groups.set([group_list])
myuser.groups.add(group, group, ...)
myuser.groups.remove(group, group, ...)
myuser.groups.clear()
myuser.user_permissions.set([permission_list])
myuser.user_permissions.add(permission, permission, ...)
myuser.user_permissions.remove(permission, permission, ...)
myuser.user_permissions.clear()
Обратите внимание: пользователь может авторизоваться напрямую.
Предположим, у вас есть приложение с именем foo и имя модели Bar, для проверки основных прав вы должны использовать:
添加:user.has_perm('foo.add_bar')
修改:user.has_perm('foo.change_bar')
删除:user.has_perm('foo.delete_bar')
查看:user.has_perm('foo.view_bar')
Разрешения по умолчанию могут быть расширены/замаскированы:
class Person(models.Model):
class Meta:
default_permissions = ()
permissions = [('can_eat_pizzas', 'Can eat pizzas')]
default_permissions = ()
заблокированPerson
дефолтadd_person
,change_person
,delete_person
иview_person
. иpermissions = [('can_eat_pizzas', 'Can eat pizzas')]
заPerson
выросcan_eat_pizzas
разрешения.
Анализ системных требований
- Роли закодированы и могут быть легко использованы в процессе программирования, например:
if user.role.has("manager") :
dosomething()
- Разрешения могут быть организованы в подкаталоги с помощью меню, например:
* 权限管理
- 用户管理
* 增加
* 编辑
* 删除
* 搜索
- 角色管理
- 权限管理
* 论坛管理
- 版面管理
* 新增版面
* 修改版面
* 查看版面
* 关闭
- 文章管理
* ...
- Разрешения могут взаимодействовать со спецификацией RESTFul для удаленного перехвата (без модели)
GET /articles/
DELETE /articles/1/
Реализация модели
Часть разрешения на операцию
- Модельная часть выглядит следующим образом:
class Permission(models.Model):
"""约定一级代表目录,二级代表页面,三级代表按钮"""
name = models.CharField(verbose_name='名称', max_length=32, blank=True, null=True)
code = models.CharField(verbose_name='编码', max_length=32, blank=True, null=True)
higher = models.ForeignKey('self', verbose_name='上级', on_delete=models.CASCADE)
url = models.CharField(verbose_name='路径', max_length=32, blank=True, null=True)
action = models.CharField(verbose_name='方法', max_length=32, blank=True, null=True)
...
class Role(models.Model):
name = models.CharField(verbose_name='名称', max_length=32, blank=True, null=True)
code = models.CharField(verbose_name='编码', max_length=32, blank=True, null=True)
permissions = models.ManyToManyField(
Permission,
verbose_name='permissions',
blank=True,
)
...
class User(AbstractUser):
roles = models.ManyToManyField(
Role,
verbose_name='roles',
blank=True,
)
...
- Перехват разрешения на операцию выглядит следующим образом:
class RBACMiddleware:
def __call__(self, request):
request_url = request.path_info
request_user = request.user
for url in settings.SAFE_URL:
if re.match(url, request_url):
pass
# 读取数据库/缓存
if has_permission_url(request_user, request_url):
pass
else:
return render(request, 'page403.html')
Раздел разрешений данных
Разрешения на данные тесно интегрированы с бизнесом, как правило, не требуется выполнять унифицированный перехват разрешений на данные, и каждый бизнес может использовать его свободно. Тем не менее, разрешения данных могут быть разделены на следующие типы, стандартизованное использование и возможность настройки.
* 行限制(根据某列的条件控制可影响的行数)
- 所有者 is_owner_required 只能够删除自己的数据行
- 协作者 is_teamworker_required 可以编辑team(部门)所属的数据行
- 受限者 is_manager_required 可以批准3天内请假
* 列限制 (控制可影响的列)
- 电话号码保密 filter_phone
- 薪资保密 filter_salary
- Часть модели
class Checker(models.Model):
CHECKER_CLAZZ = (
(1, 函数),
(2, 表达式),
)
clazz = models.CharField(verbose_name='类别', choices=CHECKER_TYPE, max_length=15, blank=True, null=True)
name = models.CharField(verbose_name='名称', max_length=32, blank=True, null=True)
code = models.CharField(verbose_name='编码', max_length=32, blank=True, null=True)
value = models.CharField(verbose_name='数值', max_length=32, blank=True, null=True)
...
- Часть реализации
Простой перехват:
def is_owner_required(model, pk_name='pk'):
def decorator(view_func):
def wrap(request, *args, **kwargs):
pk = kwargs.get(pk_name, None)
o=model.objects.get(pk=pk) #raises ObjectDoesNotExist
if o.is_owner(request.user):
return view_func(request, *args, **kwargs)
else:
raise PermissionDenied
return wraps(view_func)(wrap)
return decorator
def is_teamworker_required(model, pk_name='pk'):
def decorator(view_func):
def wrap(request, *args, **kwargs):
pk = kwargs.get(pk_name, None)
o=model.objects.get(pk=pk) #raises ObjectDoesNotExist
if o.is_teamworker(request.user):
return view_func(request, *args, **kwargs)
else:
raise PermissionDenied
return wraps(view_func)(wrap)
return decorator
@is_owner_required(Comment)
def delete_comment(request, pk):
pass
@is_teamworker_required(Comment)
def edit_comment(request, pk):
pass
Перехват сложных точек:
def is_manager_required(code, pk_name='pk'):
def decorator(view_func):
def wrap(request, *args, **kwargs):
pk = kwargs.get(pk_name, None)
o=model.objects.get(pk=pk) #raises ObjectDoesNotExist
c=checkModel.objects.get(code=code)
# check request user role limit value
if c.check(request.user):
return view_func(request, *args, **kwargs)
else:
raise PermissionDenied
return wraps(view_func)(wrap)
return decorator
@is_manager_required(code="manager_limit_3")
def audit_holiday(request, pk):
pass
Полностью динамический перехват:
def common_required(code, pk_name='pk'):
def decorator(view_func):
def wrap(request, *args, **kwargs):
pk = kwargs.get(pk_name, None)
o=model.objects.get(pk=pk) #raises ObjectDoesNotExist
c=checkModel.objects.get(code=code)
# 动态获取模块
module = __import__(c.value.module_name, fromlist=[c.value.module_class])
# 动态获取验证函数
checker = getattr(module, c.value.name)
# 执行验证函数
if checker.check(request.user, c.value.number):
return view_func(request, *args, **kwargs)
else:
raise PermissionDenied
return wraps(view_func)(wrap)
return decorator
@common_required(code="check_user_level")
def dosomething(request, pk):
pass