Django权限:权限移植

权限移植步骤

本博文以 luffy_permission 项目中写好的权限,移植到 CRM项目中为例

一、将 rbac 移植到新项目中

直接将 rbac 整个文件夹复制到 CRM 项目中

注意:

  1)rbac 中的 url 的路径,需要在整个大 url 中做路径的分发后,才能有效

path(r'^rbac/',include("rbac.urls"))

  2)由于版本1和2 的区别,url 与 path 的应用要对齐

二、settings 中 install_app 配置 rbac 的app

注入app的目的:做数据库迁移,或找templates、templatetags 能够找到

# settings
INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'app01.apps.App01Config',
   'rbac.apps.RbacConfig'
 ]

三、项目中的 UserInfo 表与 rbac 中的 User 表做一对一的关联

将项目中的用户表和 rbac 中的 user 表做一对一的绑定

# app01 / models.py

from rbac.models import User  # 引表  注意引用表的时候,如果和其他的表名重复要给个别名
class UserInfo(AbstractUser): tel = models.CharField(max_length=32, null=True, blank=True) gender = models.IntegerField(choices=((1, ""), (2, "")), default=1) depart = models.ForeignKey("Department", on_delete=models.CASCADE, default=1) user = models.OneToOneField(User, null=True,on_delete=models.CASCADE) # 与rbac中的User表进行一对一关联,由于跨app,因此需要引用过来
                                              # 记住,此处千万不要用 default="" ,应该用 null = True 否则数据库迁移的时候会报错,严重要删库

四、数据库迁移

然后做数据库的迁移,数据库迁移之后,迁移的只是权限中的表结构,没有记录,因为我们移植的是model模型类而不是数据库。

python manage.py  #cmd中才使用

makemigrations

migrate

五、admin 录入数据

查看的功能一般作为菜单权限,增删改类的不是菜单权限

菜单权限没有父权限pid,因为它自己本身就是父权限

1、自定义的在admin中输入,

  • 首先录入权限(rbac)中相关的表:

    1)菜单表

    2)权限表

    3)角色表

    4)用户表(名称和密码)

  • app01中的 user 表分配角色:

    1)userinfo表,添加角色

  注意:一定要录入userinfo表中的角色,进行关联,

 

2、另外,权限管理相关的业务完善以后

  利用完整版得权限管理,来对权限相关的表进行添加等操作,并为用户分配权限

六、修改登录函数(登录成功后权限注入session)

from rbac.service.rbac import init_permission   # 引用将权限注入session的函数
from rbac.models import User

def login(request):
    if request.method == "GET":
        return render(request, "login.html")

    else:
        user = request.POST.get("user")
        pwd = request.POST.get("pwd")
        validcode = request.POST.get("validcode")

        res = {"user": None, "error_msg": ""}
        if validcode.upper() == request.session.get("keep_str").upper():
            # user_obj = auth.authenticate(username=user, password=pwd)  # 校验数据
            user_obj = User.objects.filter(name=user,password=pwd).first()
if user_obj:
                # auth.login(request, user_obj)  # auth用户认证  一定要注释掉,否则登录时没反应
res["user"] = user # 此处不能够是user_obj ,不能是对象,可以是字符串,是对象的话,点击登录没有反应

        #注入session权限 init_permission(request,user_obj) request.session["user_id"]=user_obj.pk # 登录认证用session else: res["error_msg"] = "用户名或密码错误!" else: res["error_msg"] = "验证码错误" return JsonResponse(res)

 

1、登录时校验用户名和密码的时候是用权限中的 User 表,还是用项目中的 UserInfo 表?

  需要用权限中的 User 表来进行校验用户名和密码,

  因为注入函数,init_permission(request,user),中 user 对象是权限表中的User记录,因为该对象才有与roles绑定好的角色,如下图:

 

                    

 

2、点击登录按钮,没反应的两种可能错误

  • res["user"] = user    (此处user指的是name,非对象) 改变 res 字典返回值的时候,里面的user必须赋值是字符串形式的,不能够是user_obj对象,否则点击没反应
  • auth.login(request,user_obj)    该方法是auth 组件进行登录认证注入session的,,这里需要注释掉,因为该登陆是基于权限表中的 user 表校验用户名,直接用 request.session["user_id"] = user_obj.pk

七、在setting中配置中间件(关于权限校验的中间件)

根据中间件的路径进行配置

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.service.middlewares.PermissionMiddleWare'    # 中间件路径配置要到类名,根据自己的中间件路径来进行配置,本权限组件的路径就是此路径
]

 八、中间件添加白名单

1、将一些任何人都能访问的 url 添加到白名单中

2、"/rbac/permissions_tree/"

  本文的权限分配中的权限表的渲染是基于jquery操作渲染的,因此ajax发送的请求,也要添加到白名单中,这样任何时候都能访问了,但是分配权限的 url 不用添加到白名单。

3、注意 ajax 发送的请求,任何人都可以访问的也要放到白名单中

from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponse,redirect
import re
from rbac.models import Permission

class PermissionMiddleWare(MiddlewareMixin):

    def process_request(self,request):

        current_path = request.path

        # 设置白名单 ,"/rbac/distribute/permissions/"

        for reg in ["/login/","/admin*/","/rbac/permissions_tree/","get_valid_img","/index/"]:   # 设置/admin*/  为了使用admin后台录入数据,
ret = re.search(reg,current_path) if ret: request.show_id=0 # 只要渲染页面的时候用到母版的就会用到show_id变量,因为白名单中的路径不会走下面设置show_id,因此此处最好给设置一个值,不要为id值即可,此处只是暂时添加,等数据库中的权限表完善好了就可以不用添加, return None # 验证登录 user_id = request.session.get("user_id") if not user_id: return redirect("/login/") # 校验权限 # 1、校验权限 2、添加导航列表(做面包细) 3、添加show_id(用于判断当前url,与菜单权限的pk比较,菜单栏相应的二级菜单展开) permission_list = request.session.get("permission_list") # 路径导航列表(面包细)将其存放到request内的添加属性breadcrumb(可以随便起名)中,用于在母版layout.html中渲染 request.breadcrumb=[ { "title":"首页", "url":"/" } ] for item in permission_list: reg = "^%s$"%(item["url"]) ret = re.search(reg,current_path) if ret: show_id = item["pid"] or item["id"] # 有item["pid"](子权限url的pid值)等于item["pid"],没有值则等于item["id"](父权限的pk) request.show_id = show_id # 当前路径url的 pid ,show_id ,添加到request中临时属性中,便于后面进行比较 if item["pid"]: # 请求为子权限时 p_permission = Permission.objects.filter(pk=item["pid"]).first() # extend 是追加多个,需要放到列表中 request.breadcrumb.extend([ # 父权限字典 { "title":p_permission.title, "url":p_permission.url }, # 子权限字典 { "title":item["title"], # "url":item["url"] #由于数据库中存放的是正则的字符串,不能用item["url"],可以直接使用当前路径 "url":request.path } ]) else: # 请求为菜单父权限时,直接加入到request.breadcrumb属性中 request.breadcrumb.append({ "title": item["title"], "url": item["url"] }) return None return HttpResponse("无权访问该网页!")

九、在项目 base.html 母版中引入左侧菜单栏样式,渲染显示

1、左侧菜单中原来是在母版中写死的,需要注释或删掉,

                              

 

2、在母版中使用 模板语法来进行渲染左侧菜单栏的样式

          

{% load rbac %}
{% get_menu request %}

  分析:

    关于该语法调用的是 templatestags  / rbac / get_menu(request) 方法,该方法的用处在博文 二级菜单权限 中有详细的解释,该方法会序渲染 menu.html

from django import template

register = template.Library()  # 命名必须为 register

from django.conf import settings
import re

# 将菜单权限字典 默认传给 templates/rbac/menu.html中
@register.inclusion_tag("rbac/menu.html")  #templates是默认的不需要写
def get_menu(request):
    permission_menu_dict = request.session.get("permission_menu_dict")

    # 二级菜单发送请求的时候(或点击对应的添加、编辑等非菜单权限),该二级菜单仍然是展开的,其他一级菜单时收起的
    for val in permission_menu_dict.values():
        for item in val["children"]:
            val["class"]="hide"
            # ret = re.search("^%s$"%(item["url"]),request.path)  # 之前是只判断当前路径跟菜单权限一样展开,但是对应的添加和编辑时就不展开了,不符合需求
            print(request.show_id, item["pk"])    # 发现这里如果一级菜单下有两个二级菜单,则不符合需求,仍然需要修改
if request.show_id==item["pk"]:
                # 当前请求url的show_id 与菜单权限的pk一样时,那么该二级菜单权限是展开的
                val["class"]=""

    return {"permission_menu_dict":permission_menu_dict}

 

3、menu.html 需要在母版 base.html 中引入和更改(结合当前模板背景)

  需要渲染的 menu.html 虽然是写好的,但是样式不一定符合我们的要求,因此需要对样式进行引入和按照当前的模板颜色进行更改

                       

  1)在base.html母版中引入,由于css和js在rbac(app)下的静态文件中,这里直接使用static开始即可。

<link rel="stylesheet" href="/static/css/menu.css">   # 可能会票黄,但是不影响

...
...
...

<script src="/static/js/menu.js"></script>

 

  补充:静态文件配置

 

  2)修改css样式,据自己项目中的颜色对 menu.css 文件进行修改

    修改后的 menu.html(黑色背景):

.static-menu .icon-wrap {
    width: 20px;
    display: inline-block;
    text-align: center;
}

.static-menu a {
    text-decoration: none;
    padding: 8px 15px;
    border-bottom: 1px solid #ccc;
    color: #333;
    display: block;
    background: #efefef;
    background: -webkit-gradient(linear, left bottom, left top, color-stop(0, #efefef), color-stop(1, #fafafa));
    background: -ms-linear-gradient(bottom, #efefef, #fafafa);
    background: -moz-linear-gradient(center bottom, #efefef 0%, #fafafa 100%);
    background: -o-linear-gradient(bottom, #efefef, #fafafa);
    filter: progid:dximagetransform.microsoft.gradient(startColorStr='#e3e3e3', EndColorStr='#ffffff');
    -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#fafafa',EndColorStr='#efefef')";
    box-shadow: inset 0px 1px 1px white;
}

.static-menu a:hover {
    color: #2F72AB;
    border-left: 2px solid #2F72AB;
}


.static-menu a.active {
    color: #2F72AB;
    border-left: 2px solid #2F72AB;
}

.multi-menu .item {
    /*background-color: white;*/
    background-color: #222d32;
}

.multi-menu .item > .title {
    padding: 10px 5px;
    /*border-bottom: 1px solid #dddddd;*/
    cursor: pointer;
    /*color: #333;*/
    color: #b8c7ce;
    display: block;
    /*background: #efefef;*/
    /*background: -webkit-gradient(linear, left bottom, left top, color-stop(0, #efefef), color-stop(1,#fafafa));*/
    /*background: -ms-linear-gradient(bottom, #efefef, #fafafa);*/
    /*background: -o-linear-gradient(bottom, #efefef, #fafafa);*/
    /*filter: progid:dximagetransform.microsoft.gradient(startColorStr='#e3e3e3', EndColorStr='#ffffff');*/
    -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#fafafa',EndColorStr='#efefef')";
    /*box-shadow: inset 0 1px 1px white;*/
}

/*新添加的*/
.multi-menu .item > .title:hover{
    color: #fff;
}


.multi-menu .item > .body {
    /*border-bottom: 1px solid #dddddd;*/
    background-color: #2c3b41;
}

.multi-menu .item > .body a {
    display: block;
    padding: 5px 20px;
    text-decoration: none;
    border-left: 2px solid transparent;
    font-size: 13px;

}

.multi-menu .item > .body a:hover {
    border-left: 2px solid #2F72AB;
    color: #fff;
}

.multi-menu .item > .body a.active {
    border-left: 2px solid #2F72AB;
}
menu.html 黑色背景

    menu.js   通过一级菜单控制二级菜单的显示和隐藏

$('.item .title').click(function () {
    $(this).next().toggleClass('hide');
    $(this).parent().siblings().children(".body").addClass("hide")
});
menu.js

 

    补充:修改前的 menu.html(白色背景):这里可以供以后别的白色系项目模板使用

.static-menu .icon-wrap {
    width: 20px;
    display: inline-block;
    text-align: center;
}

.static-menu a {
    text-decoration: none;
    padding: 8px 15px;
    border-bottom: 1px solid #ccc;
    color: #333;
    display: block;
    background: #efefef;
    background: -webkit-gradient(linear, left bottom, left top, color-stop(0, #efefef), color-stop(1, #fafafa));
    background: -ms-linear-gradient(bottom, #efefef, #fafafa);
    background: -moz-linear-gradient(center bottom, #efefef 0%, #fafafa 100%);
    background: -o-linear-gradient(bottom, #efefef, #fafafa);
    filter: progid:dximagetransform.microsoft.gradient(startColorStr='#e3e3e3', EndColorStr='#ffffff');
    -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#fafafa',EndColorStr='#efefef')";
    box-shadow: inset 0px 1px 1px white;
}

.static-menu a:hover {
    color: #2F72AB;
    border-left: 2px solid #2F72AB;
}

.static-menu a.active {
    color: #2F72AB;
    border-left: 2px solid #2F72AB;
}

.multi-menu .item {
    background-color: white;
}

.multi-menu .item > .title {
    padding: 10px 5px;
    border-bottom: 1px solid #dddddd;
    cursor: pointer;
    color: #333;
    display: block;
    background: #efefef;
    background: -webkit-gradient(linear, left bottom, left top, color-stop(0, #efefef), color-stop(1, #fafafa));
    background: -ms-linear-gradient(bottom, #efefef, #fafafa);
    background: -o-linear-gradient(bottom, #efefef, #fafafa);
    filter: progid:dximagetransform.microsoft.gradient(startColorStr='#e3e3e3', EndColorStr='#ffffff');
    -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#fafafa',EndColorStr='#efefef')";
    box-shadow: inset 0 1px 1px white;
}

.multi-menu .item > .body {
    border-bottom: 1px solid #dddddd;
}

.multi-menu .item > .body a {
    display: block;
    padding: 5px 20px;
    text-decoration: none;
    border-left: 2px solid transparent;
    font-size: 13px;

}

.multi-menu .item > .body a:hover {
    border-left: 2px solid #2F72AB;
}

.multi-menu .item > .body a.active {
    border-left: 2px solid #2F72AB;
}
menu.html 白色背景

 

十、在母版(base.html)中渲染菜单路径导航 (面包屑)

在母版中添加一下代码,由于该代码是在内容区域 ,因此最好添加在  {% block  content %}的上方:

    <div class="content-wrapper">
        <!-- 添加面包屑 -->
        <div>
            <ol class="breadcrumb no-radius no-margin" style="border-bottom: 1px solid #ddd;">

                {% for item in request.breadcrumb %}
                    <li><a href="{{ item.url }}">{{ item.title }}</a></li>
                {% endfor %}

            </ol>
        </div>
    <!-- 面包屑结束 -->
<br> {% block content %}       ... {% endblock %} </div>

 

posted @ 2018-11-21 21:37  葡萄想柠檬  Views(178)  Comments(0)    收藏  举报
目录代码