一、將rbac組件拷貝到項目中。注意:
<div class="pg-body">
<div class="left-menu">
<div class="menu-body">
{# {% multi_menu request %}#}
</div>
</div>
<div class="right-body">
{# {% breadcrumb request %}#}
{% block content %} {% endblock %}
</div>
</div>INSTALLED_APPS = [
...
'rbac.apps.RbacConfig'
...
]二、將rbac/migrations目錄中的數(shù)據(jù)庫遷移記錄刪除(init.py不能刪除)三、業(yè)務系統(tǒng)中用戶表結構的設計業(yè)務表結構中的用戶表需要和rbac中的用戶表有繼承關系如:
class UserInfo(models.Model):
"""
用戶表
"""
name = models.CharField(verbose_name='用戶名', max_length=32)
password = models.CharField(verbose_name='密碼', max_length=64)
email = models.CharField(verbose_name='郵箱', max_length=32)
roles = models.ManyToManyField(verbose_name='擁有的所有角色', to=Role, blank=True)
# 去掉引號就把Role這個類的內存地址也繼承過去了,這樣做數(shù)據(jù)庫遷移就不會報錯了
def __str__(self):
return self.name
class Meta:
# django以后再做數(shù)據(jù)庫遷移時,不再為UserInfo類創(chuàng)建相關的表以及表結構了
# 此類可以當做"父類",被其他Model類繼承。里面的字段就自動過度給繼承它的類了
abstract = True注意:
from rbac.models import UserInfo as RbacUserInfo
class Host(models.Model):
"""主機表"""
hostname = models.CharField(verbose_name='主機名', max_length=32)
ip = models.GenericIPAddressField(verbose_name='IP', protocol='both')
department = models.ForeignKey(verbose_name='歸屬部門', to='Department', on_delete=models.CASCADE)
def __str__(self):
return self.hostname
class UserInfo(RbacUserInfo):
"""用戶表"""
phone = models.CharField(verbose_name='聯(lián)系方式', max_length=32)
T1 = 1
T2 = 2
T3 = 3
level_choices = (
(T1, 'T1'),
(T2, 'T2'),
(T3, 'T3')
)
level = models.IntegerField(verbose_name='級別', choices=level_choices)
department = models.ForeignKey(verbose_name='部門', to='Department', on_delete=models.CASCADE)遷移數(shù)據(jù)庫./manage.py makemigrations ./manage.py migrate 四、將業(yè)務系統(tǒng)中的用戶表的路徑寫到配置文件settings.py 在權限組件的視圖函數(shù)中將用戶表改為新的,所有的UserInfo都要改成user_model_class rbac/views/menu.py ...
from django.conf import settings
from django.utils.module_loading import import_string
...
...
def distribute_permissions(request):
"""
權限分配
:param request:
:return:
"""
user_id = request.GET.get('uid')
# user_object = models.UserInfo.objects.filter(id=user_id).first() # 之前的用戶表,不要了
# 業(yè)務中的用戶表
user_model_class = import_string(settings.RBAC_USER_MODEL_CLASS) # 自動根據(jù)字符串的形式,把這個類導入進來
user_object = user_model_class.objects.filter(id=user_id).first()
...五、業(yè)務邏輯開發(fā)1. 開發(fā)一個登陸功能2.將所有的路由都設置一個name,用來反向生成url以及粒度控制到按鈕級別的權限控制。如:from django.contrib import admin
from django.urls import path, re_path
from host.views import account
from host.views import user
from host.views import host
urlpatterns = [
re_path(r'^admin/', admin.site.urls),
re_path(r'^login/$', account.login, name='login'),
re_path(r'^logout/$', account.logout, name='logout'),
re_path(r'^index/$', account.index, name='index'),
re_path(r'^user/list/$', user.user_list, name='user_list'),
re_path(r'^user/add/$', user.user_add, name='user_add'),
re_path(r'^user/edit/(?P<pk>\d )/$', user.user_edit, name='user_edit'),
re_path(r'^user/delete/(?P<pk>\d )/$', user.user_delete, name='user_delete'),
re_path(r'^user/reset/pwd/(?P<pk>\d )/$', user.user_reset_pwd, name='user_reset_pwd'),
re_path(r'^host/list/$', host.host_list, name='host_list'),
re_path(r'^host/add/$', host.host_add, name='host_add'),
re_path(r'^host/edit/(?P<pk>\d )/$', host.host_edit, name='host_edit'),
re_path(r'^host/delete/(?P<pk>\d )/$', host.host_delete, name='host_delete'),
]六、權限信息的錄入在url中添加rbac的路由分發(fā)。注意:必須設置namesapce項目/urls.py ...
from django.urls import path, re_path, include
...
urlpatterns = [
...
path('rbac/', include(('rbac.urls', 'rbac'))),
...
]注意:rbac中的用戶管理相關的URL配置要注釋掉或刪除掉 添加菜單、分配角色、錄入權限rbac提供的地址進行操作 菜單列表 - http://127.0.0.1:8000/rbac/menu/list/ 角色列表 - http://127.0.0.1:8000/rbac/role/list/ 權限分配 - http://127.0.0.1:8000/rbac/distribute/permissions/ 1. 先添加一級菜單,然后批量錄入二級菜單和權限 2. 添加完菜單后創(chuàng)建角色 3. 最后記得分配權限,否則后面加上中間件后會提示無權限訪問 相關配置:自動發(fā)現(xiàn)URL時,排除的URL AUTO_DISCOVER_EXCLUDE = [
'/admin/',
'/login/',
'/logout/',
'/index/',
]注意:如果排除的URL中,如admin、login等用的是django2.0新出的path方法,那么正則匹配的之后需要寫成/admin.*,因為path會在admin后面加一個斜杠(\)。/admin/配置的話就會在自動發(fā)現(xiàn)URL中出現(xiàn)一推這樣的URL:/admin/ 七、編寫用戶登錄的邏輯【進行權限的初始化】def login(request):
"""
登錄
:param request:
:return:
"""
if request.method == 'GET':
return render(request, 'login.html')
user = request.POST.get('username')
pwd = request.POST.get('password')
user_obj = models.UserInfo.objects.filter(name=user, password=pwd).first()
if not user_obj:
return render(request, 'login.html', {'error': '用戶名或密碼錯誤'})
# 用戶權限信息的初始化
init_permission(user_obj, request)
return redirect(reverse('index'))相關配置:權限和菜單的session key: PERMISSION_SESSION_KEY = 'permission_url_list_key' MENU_SESSION_KEY = 'permission_menu_key' 八、編寫一個首頁的邏輯def index(request):
return render(request, 'index.html')相關配置: # 需要登錄,但無需權限的URL
NO_PERMISSION_LIST = [
'/logout/',
'/index/',
]在中間件新增無需權限校驗,但是需要登錄才能訪問的功能 ...
url_record = [
{'title': '首頁', 'url': '#'}
]
# 此處代碼進行判斷:/logout/,/index/ 無需權限校驗,但是需要登錄才能訪問
for url in settings.NO_PERMISSION_LIST:
if re.match(url, request.path_info):
# 需要登錄,但無需權限校驗
request.current_selected_permission = 0 # 等于0就是沒有默認選中,和菜單沒有關聯(lián)上,就不會做默認展開
request.breadcrumb = url_record
return None
has_permission = False
...九、通過中間件進行權限的校驗rbac/middlewares/rabc.py # 權限校驗
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'rbac.middlewares.rbac.RbacMiddleware',
]白名單 # 白名單,無需登錄就可以訪問 WHITE_LIST = ['/login/', '/admin/.*'] 十、粒度到按鈕級別的控制host/templates/user_list.html {% extends 'layout.html' %}
{% load rbac %}
{% block content %}
<h1>用戶列表</h1>
<div class="custom-container">
<div class="btn-group" style="margin: 5px 0">
{% if request|has_permission:'user_add' %} <!-- 控制增加按鈕 -->
<a class="btn btn-default" href="{% memory_url request 'user_add' %}">
<i class="fa fa-plus-square" aria-hidden="true"></i> 添加用戶
</a>
{% endif %}
</div>
<table class="table table-bordered table-hover">
<thead>
<tr>
<th>用戶名</th>
<th>郵箱</th>
<th>級別</th>
<th>部門</th>
<th>手機</th>
{% if request|has_permission:'user_reset_pwd' %} <!-- 控制重置密碼按鈕 -->
<th>重置密碼</th>
{% endif %}
{% if request|has_permission:'user_edit' or 'user_delete' %}
<th>操作</th>
{% endif %}
</tr>
</thead>
<tbody>
{% for row in user_queryset %}
<tr>
<td>{{ row.name }}</td>
<td>{{ row.email }}</td>
<td>{{ row.get_level_display }}</td>
<td>{{ row.department.title }}</td>
<td>{{ row.phone }}</td>
{% if request|has_permission:'user_reset_pwd' %}
<td>
<a href="{% memory_url request 'user_reset_pwd' pk=row.id %}">重置密碼</a>
</td>
{% endif %}
{% if request|has_permission:'user_edit' or 'user_delete' %} <!-- 控制編輯和刪除按鈕 -->
<td>
{% if request|has_permission:'user_edit' %}
<a style="color: #333333; font-size:18px"
href="{% memory_url request 'user_edit' pk=row.id %}">
<i class="fa fa-edit" aria-hidden="true"></i></a>
{% endif %}
{% if request|has_permission:'user_delete' %}
<a style="color: red; font-size:18px"
href="{% memory_url request 'user_delete' pk=row.id %}">
<i class="fa fa-trash-o" aria-hidden="true"></i></a>
{% endif %}
</td>
{% endif %}
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endblock content %}總結:目的是希望在任意系統(tǒng)中應用權限系統(tǒng)
INSTALLED_APPS = [
...
'host.apps.HostConfig',
'rbac.apps.RbacConfig'
...
]
MIDDLEWARE = [
...
'rbac.middlewares.rbac.RbacMiddleware',
...
]
# 業(yè)務中的用戶表
RBAC_USER_MODEL_CLASS = 'host.models.UserInfo'
# 權限在session中存儲的key
PERMISSION_SESSION_KEY = 'permission_url_list_key'
# 菜單在Session中存儲的key
MENU_SESSION_KEY = 'permission_menu_key'
# 白名單
WHITE_LIST = ['/login/', '/admin/.*']
# 自動發(fā)現(xiàn)路由中URL時,排除的URL
AUTO_DISCOVER_EXCLUDE = [
'/admin.*',
'/login.*',
'/logout.*',
'/index.*',
]
# 需要登錄,但無需權限的URL
NO_PERMISSION_LIST = [
'/logout/',
'/index/',
]
|
|
|