ACL 是 Access Control List 的縮寫,稱爲訪問控制列表,包含了對一個對象或一條記錄可進行何種操做的權限定義。html
例如一個文件對象的 ACL 爲 { "Alice": { "read": true, "write": true }, "Bob": { "read": true } },web
這表明 Alice 對該文件既能讀又能寫,而 Bob 只能讀取。數據庫
RBAC基於角色的權限訪問控制(Role-Based Access Control)不一樣於賦予使用者權限,而是將權限賦予角色。django
RBAC模型中「權限」只和「角色」對應,而用戶也和「角色」對應,爲用戶賦予角色,而後管理角色的權限,完成了權限與用戶的解耦。編程
RBAC0主要特色是:緩存
用戶和角色之間是多對一仍是多對多的關係。bash
RBAC1主要特色是:函數
角色能夠繼承,造成樹狀。測試
RBAC2主要特定是:ui
角色能夠互斥。(出納和會計) 基數約束。(ceo) 先決條件。(逐層升級) 運行時互斥。(運行時只容許一個角色,水的三態)
RBAC3,統一rabac1和rbac2。
object (row) level permissions
model (table) level permissions
複製代碼
操做權限: web系統頁面的菜單和按鈕 數據權限: web系統中對數據記錄操做
Django 帶有一個簡單的權限系統。它提供了爲指定的用戶和用戶組分配權限的方法。
這裏的group和角色實際上爲一個概念
User 對象有兩個多對多字段:groups 和 user_permissions。 User 對象能夠像訪問其餘 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()
複製代碼
* 權限管理
- 用戶管理
* 增長
* 編輯
* 刪除
* 搜索
- 角色管理
- 權限管理
* 論壇管理
- 版面管理
* 新增版面
* 修改版面
* 查看版面
* 關閉
- 文章管理
* ...
複製代碼
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
複製代碼