Role Based Access Control,基于角色的權(quán)限控制,用于計(jì)算機(jī)操作系統(tǒng)的安全性控制
組件
多個(gè)插件組合到一起的部件,用于實(shí)現(xiàn)一系列相關(guān)功能. 例如:form組件
插件
僅僅實(shí)現(xiàn)一個(gè)小功能 比如: 分頁
權(quán)限組件
實(shí)現(xiàn)思路: 用戶--->職位(角色)--->權(quán)限
表結(jié)構(gòu)設(shè)計(jì)
1. 僅設(shè)計(jì) 權(quán)限表和用戶表
權(quán)限表 id url
用戶表 id name pwd .....
[ 關(guān)系:多對多, 一個(gè)人要有多個(gè)權(quán)限( 查看,修改的權(quán)限 ),且一個(gè)權(quán)限可以被多個(gè)人使用 ]
缺點(diǎn):這種關(guān)系是繁瑣的:一個(gè)職員的離職和入職,都需要?jiǎng)h除和添加這個(gè)職員和權(quán)限的關(guān)系
2.我們引入了角色的概念
權(quán)限表 路由 標(biāo)題
用戶表 姓名 密碼
角色表 名稱
[ 用戶和角色是多對多的關(guān)系(要有兼職的概念) ; 權(quán)限和角色也是多對多的關(guān)系(BOSS和銷售都要有 查 的權(quán)限),而BOSS不可能只有 查 的權(quán)限 ]
一定要注意:權(quán)限的重疊
流程
- 首先要記錄每個(gè)用戶的權(quán)限
- 登錄注冊功能
- 用戶登錄的時(shí)候,校驗(yàn)用戶的路由
- 登錄完成時(shí),將用戶的權(quán)限儲(chǔ)存到session中,方便快速獲取校驗(yàn)
- 登錄完成后,要校驗(yàn)用戶訪問的路由,不能超出權(quán)限,所以我們要利用中間件,在訪問之前完成校驗(yàn)
- 如果超出權(quán)限,拋出異常
實(shí)踐
記錄用戶權(quán)限
在 django 項(xiàng)目中創(chuàng)建并注冊一個(gè)app
簡單示例:
class Permission(models.Model):
'''
權(quán)限表
'''
url = models.CharField('權(quán)限', max_length=32)
title = models.CharField('標(biāo)題', max_length=32)
def __str__(self):
return self.title
class Role(models.Model):
'''
角色表
'''
name = models.CharField('角色名稱', max_length=32)
permissions = models.ManyToManyField('Permission', verbose_name='角色擁有的權(quán)限', blank=True)
def __str__(self):
return self.name
class User(models.Model):
'''
用戶表
'''
name = models.CharField('用戶名', max_length=32)
pwd = models.CharField('密碼', max_length=32)
roles = models.ManyToManyField('Role', verbose_name='用戶擁有的角色', blank=True)
def __str__(self):
return self.name
我們進(jìn)入應(yīng)用下的 admin 文件中,用Django 提供的基于 web 的管理工具
from django.contrib import admin
from rbac import models # 導(dǎo)入表結(jié)構(gòu)
class PermissionAdmin(admin.ModelAdmin):
list_display = ['url', 'title', ] # 顯示字段
list_editable = ['title', ] # 修改字段
# 創(chuàng)建的三個(gè)表
admin.site.register(models.Permission, PermissionAdmin)
admin.site.register(models.Role)
admin.site.register(models.User)
隨后登錄 admin 錄入數(shù)據(jù)
登錄
def login(request):
'''登錄'''
msg = ''
if request.method == 'POST':
user = request.POST.get('username')
pwd = request.POST.get('pwd')
# 通過用戶名和密碼找到 用戶對象
user_obj= models.User.objects.filter(name=user,pwd=pwd).first()
if user_obj:
# 進(jìn)行 ORM 操作
通過對象找到角色,再通過角色找到權(quán)限.. '注意要去重:去除重復(fù)權(quán)限'
ret = obj.roles.filter(permissions__url__isnull=False).values( xxx ).distinct()
# 將權(quán)限列表保存到session中
request.session[settings.PERMISSION_SESSION_KEY] = permission_list
return redirect('customer_list')
msg = '用戶名或密碼錯(cuò)誤'
return render(request,'login.html',{'msg':msg})
校驗(yàn)
在用戶訪問之前判斷是否超出自己的權(quán)限,所以我們將權(quán)限的校驗(yàn),放置到中間件中
- 獲取當(dāng)前訪問的路由
- 設(shè)置白名單
- 判斷是否登錄,如果沒有重定向到登錄界面
- 免認(rèn)證( 有一些頁面,需要登錄但是不需要校驗(yàn),比如游客瀏覽的首頁 )
- 獲取當(dāng)前用戶的權(quán)限信息
- 循環(huán)校驗(yàn)
1)對比成功 有權(quán)限 return
2)不成功 沒有權(quán)限 return HttpResponse(‘沒有權(quán)限’)
其中第3、4 步可以省略不寫;還有注意中間件的注冊
from django.utils.deprecation import MiddlewareMixin # 導(dǎo)入中間件
from django.shortcuts import HttpResponse
from django.conf import settings # 將session的key保存到配置文件中,做常量,防止修改,而獲取不到
import re # 進(jìn)行正則匹配
class Rbac_Middlewar(MiddlewareMixin):
def process_request(self, request):
# 獲取當(dāng)前的路由
url = request.path_info
# 白名單( 在 settings 中)
for i in settings.WHITE_LIST:
if re.match(i, url):
retur
# 獲取當(dāng)前用戶的權(quán)限
Permission_list = request.session.get(settings.PERMISSION_SESSION_KEY)
# 進(jìn)行校驗(yàn)
for i in Permission_list:
if re.match('^{}$'.format(i["url"]), url):
return
return HttpResponse('校驗(yàn)shibai')
注意事項(xiàng)
- form表單的 input 框一定要有名字,否則你無法獲取數(shù)據(jù)的內(nèi)容
- ORM 的增刪改查
- 在錄入權(quán)限的路由時(shí),注意輸入含有正則的字符串
比如 修改權(quán)限: 路由可以寫作 /customer/edit/(\d+)/ , \d+ 可以表示被修改者的id
- 中間件的注冊
- re模塊的應(yīng)用 math( 正則,字符串 )
- 當(dāng)將 session 的鍵,作為常量寫到 settings 配置文件中時(shí),那么在模板中,無法獲取到用戶的權(quán)限,因?yàn)?模板語法僅支持點(diǎn)語法,沒有 [ ] 的概念 ,所以我們可以通過 inclusion_tag 來返回自定義標(biāo)簽
# 將權(quán)限列表保存到session中
request.session[settings.PERMISSION_SESSION_KEY] = permission_list
# inclusion_tag 流程
1. 創(chuàng)建一個(gè)
2. 創(chuàng)建一個(gè) 自定義py文件
3. 進(jìn)行編寫基礎(chǔ)代碼
from django import template
register = template.Library()
@register.inclusion_tag('menu.html')
def menu(request):
menu_dic = request.session.get(settings.MENU_SESSION_KEY)
print(menu_dic)
return {'menu_dic': menu_dic}