Django 基於角色的許可權控制
ENV: Python3.6 + django1.11
應用場景
有一種場景, 要求為使用者賦予一個角色, 基於角色(比如後管理員,總編, 編輯), 使用者擁有相應的許可權(比如管理員擁有所有許可權, 總編可以增刪改查, 編輯只能增改, 有些頁面的按鈕也只有某些角色才能檢視), 角色可以任意新增, 每個角色的許可權也可以任意設定
django 的許可權系統
django 預設的許可權是基於Model
的add, change, delete來做許可權判斷的, 這種設計方式有一個明顯的缺陷, 比如怎麼控制對Model
的某個欄位的修改的許可權的控制呢
設計許可權
大多數的系統, 都會給使用者賦予某個角色, 假如能針對使用者的角色, 做許可權控制,這個許可權控制並不侷限於對Model
的修改, 可以是任意位置的許可權控制, 只要有一個許可權名, 即可根據使用者角色名下是否擁有許可權名判斷是否擁有許可權
User, Role => UserRole => RolePermissions
User 是使用者物件
Role: 角色表
UserRole 是使用者角色關係物件
RolePermissions 是角色許可權關係物件
因此, 需要建立三個Model
: User
Role
, UserRole
, RolePermission
User
可以使用django 預設的User
物件
其他Model 如下
class Role(models.Model): """角色表""" # e.g add_user role_code = models.CharField('role code', max_length=64, unique=True, help_text = '使用者角色標識') # e.g 新增使用者 role_name = models.CharField('role name', max_length=64, help_text = '使用者角色名') class UserRole(models.Model): """使用者角色關係表""" user_id = models.IntegerField('user id', blank=False, help_text='使用者id', unique=True) role_codes = models.CharField('role codes', blank=True, default=None, max_length=256, help_text='使用者的角色codes') class RolePermission(models.Model): """角色許可權關係表""" role_code = models.CharField('role code', max_length=64, blank=False, help_text = '使用者角色標識') pm_code = models.CharField('permission code', blank=False, max_length=64, help_text='許可權code') class Meta: unique_together = ('role_code', 'pms_code')
其中 Role
和 RolePermission
用於管理角色和對應的許可權的關係
UserRole
用於管理使用者和角色的對映關係
許可權管理
使用者角色擁有哪些許可權是在程式碼裡定義好的, 比如:
PMS_MAP = (
('PM_ADD_USER', '新增使用者'), ('PM_SET_MAIL', '編輯郵箱'), ... )
PM_ADD_USER
是許可權code碼, 新增使用者
是許可權名, 在這裡, 許可權名由我們定義, 後面在需要使用的地方做has_perm(<pm_coede>)
判斷時, 用的就是這是這個code
角色管理
在定義好許可權後, 我們就可以做角色管理了,
在這裡, 我們可以建立任意的角色, 為其分配任意的許可權, 當然, 最好建立有意義的角色
角色表單定義(forms.py
)
role_regex_validator = RegexValidator(r"[a-zA-Z0-9]", "角色標記只能包含字母,數字, 下劃線") class RoleForm(forms.Form): role_row_code = forms.IntegerField(required=False, widget=forms.HiddenInput()) role_code = forms.CharField(label='角色標記', min_length=3, max_length=64, validators=[role_regex_validator]) role_name = forms.CharField(label='角色名', min_length=3, max_length=64) OPTIONS = PMS_MAP pms = forms.MultipleChoiceField(label='許可權列表', widget=forms.SelectMultiple(choices=OPTIONS)
角色編輯views.py
def role_edit(request):
"""角色編輯""" if request.method == 'POST': role_row_id = request.POST.get('role_row_id', 0) role_code = request.POST.get('role_code', '') role_name = request.POST.get('role_name', '') pms = request.POST.getlist('pms', []) # 表單校驗 role_form = RoleForm({ 'role_row_id': role_row_id, 'role_code': role_code, 'role_name': role_name, 'pms': pms }) # 表單校驗 if not role_form.is_valid(): return render(request, 'role_form.html', {'form': role_form) role_row_id = role_form.cleaned_data.get('role_row_id', None) if role_row_id: # 角色更新 return update_role(request, role_form, role_row_id=role_row_id, role_code=role_code, role_name=role_name, pms=pms) else: # 角色建立 return add_role(request, role_form, role_code, role_name, pms=pms) else: # 角色編輯頁面 role_row_id = request.GET.get('id') try: role_item = Role.objects.get(pk=role_row_id) except Role.DoesNotExist as e: role_item = None if role_item: # 編輯已有角色表單 # 獲取角色許可權列表 role_pms_rows = RolePermission.objects.filter(role_code=role_item.role_code) pms_codes = [role_pms_row.pms_code for role_pms_row in role_pms_rows] role_form = RoleForm({ 'role_row_id': role_row_id, 'role_code': role_item.role_code, 'role_name': role_item.role_name, 'pms':