rbac - 界面、權限

1、模板繼承

 

知識點:css

users.html / roles.html 繼承自 base.html
滑動時,固定
position: fixed;top:60px;bottom:0;left:0;width:200px;
overflow: auto;

base.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>base</title>
    <!-- 最新版本的 Bootstrap 核心 CSS 文件 -->
    <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
    <style type="text/css">
        *{padding: 0;margin: 0;}
        .header{width: 100%; height: 60px;background-color: #336699;}
        .menu{background-color: bisque; position: fixed; top:60px;bottom: 0;left: 0; width: 200px;}
        .content{ position: fixed;top: 60px;bottom: 0; right: 0; left: 200px;overflow: auto;padding: 20px;}

    </style>

</head>
<body>

<div class="header">
<p>{{ user.name }}</p>
</div>

<div class="container">
    <div class="menu">
        menu
    </div>

    <div class="content">
        {% block con %}

        {% endblock con%}
    </div>
</div>

</body>
</html>

 

users.htmlhtml

{% extends 'base.html' %}

{% block con %}
<h4>用戶列表</h4>
    {% for user in user_list %}
    <p>{{ user }}</p>
    {% endfor %}
    
{% endblock con%}

2、table

 

知識點:git

{% for user in user_list %}
<tr>
<td>{{ forloop.counter }}</td>
<td>{{ user.name }}</td>
<td>
{% for role in user.roles.all %}
  {{ role.title }}
{% endfor %}
</td>
<td>
<a href="" class="btn btn-danger">刪除</a>
<a href="" class="btn btn-warning">編輯</a>
</td>
</tr>
{% endfor %}

 

users.htmlgithub

{% extends 'base.html' %}

{% block con %}
    <h4>用戶列表</h4>
    <a href="" class="btn btn-primary">添加</a>
    <table class="table table-bordered table-striped">
        <thead>
            <tr>
                <th>序號</th>
                <th>姓名</th>
                <th>角色</th>
                <th>操做</th>
            </tr>
        </thead>
        <tbody>
        {% for user in user_list %}
            <tr>
                <td>{{ forloop.counter }}</td>
                <td>{{ user.name }}</td>
                <td>
                    {% for role in user.roles.all %}
                    {{ role.title }}
                    {% endfor %}
                </td>
                <td>
                    <a href="" class="btn btn-danger">刪除</a>
                    <a href="" class="btn btn-warning">編輯</a>
                </td>
            </tr>
        {% endfor %}

        </tbody>

    </table>

{% endblock con%}

3、權限不一樣(按鈕 。。。)

用戶權限不一樣,按鈕顯示就不一樣! 
登陸成功後,就已經註冊了session
request.session['permission_list'] = permission_list

permission_list = request.session.get('permission_list')

0.
{% if "/users/add/" in permission_list %}
<a href="/users/add/" class="btn btn-primary">添加</a>
{% endif %}

{% if '/users/delete/(\d+)/' in permission_list %}
<a href="/users/delete/{{ user.pk }}/" class="btn btn-danger">刪除</a>
{% endif %}

BUT: 很差,不想讓 if "/users/add/" 寫死,會有 "/roles/add/" 狀況,不健壯!怎麼辦?
不該該根據表名,去判斷!!
權限不一樣,按鈕顯示就不一樣 如何作呢?    
上面問題的解決辦法:
  爲了擴展,
  # 把兩條線 合成一個線
  /users/..
   /roles/...

1.修改表結構 
class Permission(models.Model):
title = models.CharField(max_length=32)
url = models.CharField(max_length=32)

# 標記出行爲
action = models.CharField(max_length=32, default="")

group = models.ForeignKey('PermissionGroup', default=1, on_delete=True)

def __str__(self):
return self.title

class PermissionGroup(models.Model):
title = models.CharField(max_length=32)

def __str__(self):
return self.title

makemigrations
migrate

注意點:

加了一個權限組表
將每張表的增刪改查,劃到一個組裏面!
不管多複雜的,最終必定是對數據庫的(增刪改查

修改表結構,從新處理中間件,登陸頁面:
目的:全是爲了按鈕的粒度,同一個模板,同一個視圖,
顯示不一樣的數據,權限

models.pyajax

from django.db import models

class User(models.Model):
    name = models.CharField(max_length=32)
    pwd = models.CharField(max_length=32)
    roles = models.ManyToManyField(to='Role')

    def __str__(self):
        return self.name

class Role(models.Model):
    title = models.CharField(max_length=32)
    permission = models.ManyToManyField(to='Permission')

    def __str__(self):
        return self.title

class Permission(models.Model):
    title = models.CharField(max_length=32)
    url = models.CharField(max_length=32)

    # 標記出行爲
    action = models.CharField(max_length=32, default="")

    group = models.ForeignKey('PermissionGroup', default=1, on_delete=True)

    def __str__(self):
        return self.title

class PermissionGroup(models.Model):
    title = models.CharField(max_length=32)

    def __str__(self):
        return self.title

 

2.admin 修改

注意:list_display = []


admin.py
from django.contrib import admin
from .models import *

class PerConfig(admin.ModelAdmin):
    list_display = ['title','url','group','action']

admin.site.register(User)
admin.site.register(Role)
admin.site.register(Permission,PerConfig)
admin.site.register(PermissionGroup)

 

3.登陸以後,重寫 initial_session(user,request)
就是:
# 在session中註冊權限列表 用戶權限
# request.session['permission_list'] = permission_list

不該該是list 而是dict

# 在session中註冊權限字典
request.session['permission_dict'] = permission_dict

 注意點:數據庫

permission = user.roles.all().values('permission__url', 'permission__group_id', 'permission__action').distinct()

  對數據的處理,以組爲鍵django

 
{1: {'urls': ['/users/', '/users/add/', '/users/delete/(\\d+)/', '/users/edit/(\\d+)/'], 
     'actions': ['list', 'add', 'delete', 'edit']}, 
 2: {'urls': ['/roles/'], 
     'actions': ['list']}}

permission.py
# -*- coding:utf-8 -*-

def initial_session(user, request):
    # 方案一
    # permission = user.roles.all().values('permission__url').distinct()
    # # print(permission)
    # # 去重後的  全部權限!! 將權限 存在 session 中!!
    # # <QuerySet [{'permission__url': '/users/'}, {'permission__url': '/users/add'}]>
    #
    # permission_list = []
    # for item in permission:
    #     permission_list.append(item['permission__url'])
    #
    # print(permission_list)  # ['/users/', '/users/add']
    #
    # # 在session中註冊權限列表   用戶權限
    # request.session['permission_list'] = permission_list

    # 方案二
    permission = user.roles.all().values('permission__url', 'permission__group_id', 'permission__action').distinct()

    print('permission:', permission)

    # permission: <QuerySet [
    # {'permission__url': '/users/',
    # 'permission__group_id': 1,
    # 'permission__action': 'list'},

    # {'permission__url': '/users/add/',
    # 'permission__group_id': 1,
    # 'permission__action': 'add'},

    # {'permission__url': '/users/delete/(\\d+)/',
    # 'permission__group_id': 1,
    # 'permission__action': 'delete'},

    # {'permission__url': '/users/edit/(\\d+)/',
    # 'permission__group_id': 1,
    # 'permission__action': 'edit'}]>

    # {'permission__url': 'roles/',
    # 'permission__group_id': 2,
    # 'permission__action': 'list'}]>

    # 處理數據 : 以組爲鍵
    """
    1:{
        "url":['/users/','/users/add','/users/delete/(\\d+)/','/users/edit/(\\d+)']
        "action":['list','add','delete','edit']
    }

    2:{
        "url":['/roles/']
        "action":['list']  
    }


    """

    permission_dict = {}
    for item in permission:
        gid = item.get('permission__group_id')
        url = item.get('permission__url')
        action = item.get('permission__action')

        if not gid in permission_dict.keys():
            permission_dict[gid] = {
                "urls": [url, ],
                "actions": [action, ]
            }

        else:
            permission_dict[gid]['urls'].append(url)
            permission_dict[gid]['actions'].append(action)

    print(permission_dict)
    """
    {1: {'urls': ['/users/', '/users/add/', '/users/delete/(\\d+)/', '/users/edit/(\\d+)/'], 
        'actions': ['list', 'add', 'delete', 'edit']}, 
    2: {'urls': ['/roles/'], 
        'actions': ['list']}}
    """

    # 註冊session
    # # 在session中註冊權限字典   用戶權限

    request.session['permission_dict'] = permission_dict

 

4.重寫中間件
校驗權限

# 注意: !!
request.actions = item["actions"]

rbac.py
# -*- coding:utf-8 -*-
from django.shortcuts import HttpResponse, render, redirect
from django.utils.deprecation import MiddlewareMixin
import re

class ValidPermission(MiddlewareMixin):

    def process_request(self,request):

        current_path = request.path_info

        # 白名單,不須要任何權限的url
        valid_url_list = ['/login/', '/reg/', '/admin/.*']

        for valid_url in valid_url_list:
            ret = re.match(valid_url, current_path)
            if ret:
                return

        # 校驗是否登陸
        user_id = request.session.get('user_id')
        if not user_id:
            return redirect('/login/')

        # # 校驗權限 1
        # permission_list = request.session.get('permission_list',[])
        #
        # flag = False
        # for permission in permission_list:
        #     # ['/users/', '/users/add/', '/users/edit/(\\d+)/', '/users/delete/(\\d+)/']
        #     # 須要 ^ $ 限定!!
        #     permission = "^%s$" % permission
        #
        #     ret = re.match(permission, current_path)
        #     if ret:
        #         flag = True
        #         break
        #
        # if not flag:
        #     return HttpResponse('無訪問權限!')

        # 校驗權限 2 permission_dict
        permission_dict = request.session.get('permission_dict')
        for item in permission_dict.values():
            urls = item['urls']
            for reg in urls:
                reg = "^%s$" % reg
                ret = re.match(reg, current_path)
                if ret:
                    print("action", item['actions'])

                    # 注意:妙 !!
                    request.actions = item["actions"]

                    return

        return HttpResponse('無權訪問')


"""
permission_dict:

{1: {'urls': ['/users/', '/users/add/', '/users/delete/(\\d+)/', '/users/edit/(\\d+)/'], 
    'actions': ['list', 'add', 'delete', 'edit']}, 
2: {'urls': ['/roles/'], 
    'actions': ['list']}}

"""

 

5.重寫users()視圖,以及users.html

1.也能夠實現:
{% if "add" in request.actions %}
<a href="/users/add/" class="btn btn-primary">添加</a>
{% endif %}

BUT: 還能夠更好:用來實現!!

{% if per.add %}
<a href="/users/add/" class="btn btn-primary">添加</a>
{% endif %}

{% if per.delete %}
<a href="" class="btn btn-danger">刪除</a>
{% endif %}
-------
per = Per(request.actions)

class Per(object):
def __init__(self,actions):
self.actions = actions

def add(self):
return "add" in self.actions

def delete(self):
return "delete" in self.actions

def edit(self):
return "edit" in self.actions

def list(self):
return "list" in self.actions

views.py
from django.shortcuts import render,HttpResponse,redirect
import re

from rbac.models import *
from rbac.service.permission import initial_session

class Per(object):
    def __init__(self,actions):
        self.actions = actions

    def add(self):
        return "add" in self.actions

    def delete(self):
        return "delete" in self.actions

    def edit(self):
        return "edit" in self.actions

    def list(self):
        return "list" in self.actions



def users(request):
    user_list = User.objects.all()

    user_id = request.session.get('user_id')
    user = User.objects.filter(id=user_id).first()

    per = Per(request.actions)

    return render(request,'users.html',locals())

 

users.htmlbootstrap

{% extends 'base.html' %}

{% block con %}
    <h4>用戶列表</h4>

{#    {% if "/users/add/" in permission_list %}#}
{#    {% if "add" in request.actions %}#}
    {% if per.add %}
        <a href="/users/add/" class="btn btn-primary">添加</a>
    {% endif %}

    <table class="table table-bordered table-striped">
        <thead>
            <tr>
                <th>序號</th>
                <th>姓名</th>
                <th>角色</th>
                <th>操做</th>
            </tr>
        </thead>
        <tbody>
        {% for user in user_list %}
            <tr>
                <td>{{ forloop.counter }}</td>
                <td>{{ user.name }}</td>
                <td>
                    {% for role in user.roles.all %}
                    {{ role.title }}
                    {% endfor %}
                </td>
                <td>
                    {% if per.delete %}
                         <a href="" class="btn btn-danger">刪除</a>
                    {% endif %}
                    {% if per.edit %}
                        <a href="" class="btn btn-warning">編輯</a>
                    {% endif %}
                </td>
            </tr>
        {% endfor %}

        </tbody>

    </table>

{% endblock con%}

4、效果

不一樣的用戶,具備不一樣的權限,瀏覽器

權限不一樣,顯示的按鈕就不一樣!!session

 

 
權限粒度控制

簡單控制:
    {% if "users/add" in permissions_list%}

擺脫表控制

更改數據庫結構
    class Permission(models.Model):
        title=models.CharField(max_length=32)
        url=models.CharField(max_length=32)

        action=models.CharField(max_length=32,default="")
        group=models.ForeignKey("PermissionGroup",default=1)
        def __str__(self):return self.title

    class PermissionGroup(models.Model):
        title = models.CharField(max_length=32)

        def __str__(self): return self.title

登陸驗證:
    permissions = user.roles.all().values("permissions__url","permissions__group_id","permissions__action").distinct()

    構建permission_dict

        permissions:
            [

             {'permissions__url': '/users/add/',
             'permissions__group_id': 1,
             'permissions__action': 'add'},

             {'permissions__url': '/roles/',
             'permissions__group_id': 2,
             'permissions__action': 'list'},

             {'permissions__url': '/users/delete/(\\d+)',
             'permissions__group_id': 1,
             'permissions__action': 'delete'},

             {'permissions__url': 'users/edit/(\\d+)',
             'permissions__group_id': 1,
             'permissions__action': 'edit'}
             ]

        permission_dict
             {

             1: {
             'urls': ['/users/', '/users/add/', '/users/delete/(\\d+)', 'users/edit/(\\d+)'],
             'actions': ['list', 'add', 'delete', 'edit']},

             2: {
             'urls': ['/roles/'],
             'actions': ['list']}

             }

中間件校驗權限:
    permission_dict=request.session.get("permission_dict")

    for item in permission_dict.values():
          urls=item['urls']
          for reg in urls:
              reg="^%s$"%reg
              ret=re.match(reg,current_path)
              if ret:
                  print("actions",item['actions'])
                  request.actions=item['actions']
                  return None

    return HttpResponse("沒有訪問權限!")

思考:
    菜單權限顯示
筆記

5、權限不一樣(菜單。。。)

權限不一樣,菜單欄不一樣

只有查看,有必要放到菜單欄!
即:action == list 放到 菜單欄中

1.用戶登陸後,在initial_session中,註冊菜單權限

注意:permission__group__title 還能夠這樣用,跨了3張表!!

permissions = user.roles.all().values('permission__url', 'permission__action',
'permission__group__title').distinct()

menu_permission_list = []
for item in permissions:
if item['permission__action'] == 'list':
menu_permission_list.append((item['permission__url'], item['permission__group__title']))

print(menu_permission_list)
# [('/users/', '用戶管理'), ('/roles/', '角色管理')]

request.session['menu_permission_list'] = menu_permission_list
2.menu
menu_permission_list = request.session.get('menu_permission_list')

base.html
{% for item in menu_permission_list %}
<p class="menu_btn"><a href="{{ item.0 }}">{{ item.1 }}</a></p>
{% endfor %}

能夠實現,菜單顯示!可是不行,爲何?

由於模板繼承,只繼承樣式,不繼承數據!全部須要用到 自定義標籤(inclusion_tag)

3.自定義標籤(inclusion_tag)
rbac/templatetags/my_tags.py
from django import template
register = template.Library()

@register.inclusion_tag('menu.html')
def get_menu(request):
# 獲取當前用戶,應該放到菜單欄的權限
menu_permission_list = request.session.get('menu_permission_list')

return {'menu_permission_list':menu_permission_list}

menu.html:
<div>
{% for item in menu_permission_list %}
<p class="menu_btn"><a href="{{ item.0 }}">{{ item.1 }}</a></p>
{% endfor %}

</div>

base.html
{% load my_tags %}
<div class="menu">
{% get_menu request %}
</div>
4.包...建在哪一個App
屬於權限的就建在rbac的APP裏,由於rpac最後是可插拔的組件!!

users.html / roles.html / base.html / menu.html
是和權限相關的,因此應該放在 rbac/templates/... 方便之後copy走!!

djangod的render去渲染 .html 時,先到項目的 templates 下找,找不到,再到App下 templates 下找,
最後找不到,才報錯!!

若是多個App的templates 下的.html重名怎麼辦? django 會根據註冊的順序顯示!
解決辦法:
項目/rbac/templates/rbac/xxx.html

這時調用:
return render(request, 'rbac/users.html', locals())

注意:
templates 或者 templatetag 注意多個app下面 的文件名 有可能都會重名!!
辦法:就是 eg:/rbac/templates/rbac/xxx.html 或者不起重名

注意:
若是 base.html 在項目下有,在App下有,先找項目下的,找不到才找App
全局能夠覆蓋局部的!!

6、效果

用戶的權限不一樣,

顯示的菜單欄,就不一樣

按鈕也不一樣

 

7、路徑自動添加

知識點:路徑自動添加問題:
http://127.0.0.1:8010/users
http://127.0.0.1:8010/users/

瀏覽器發請求:
django 發現以後,發了一個重定向的 url 加了一個 /
全部才能匹配上:
path('users/', views.users),

如何讓django不給瀏覽器發重定向,不加 /
配置:
APPEND_SLASH = False

APPEND_SLASH = True # 默認爲 True

ajax 也是,django會默認的加 / 發重定向

8、rbac_code

原始版 

https://github.com/alice-bj/rbac_pro_0

相關文章
相關標籤/搜索