权限(二)

本篇阅读目录

  • 一、权限组件
  • 二、动态显示权限菜单(单级菜单)
  • 三、补充知识点

回到顶部

一、权限组件

1、上篇随笔中,我们只是设计好了权限控制的表结构,有三个模型,五张表,两个多对多关系,并且简单实现了对用户的权限控制,我们会发现那样写有一个问题,就是权限控制写死在了项目中,并且没有实现与我们的业务逻辑解耦,当其他项目要使用权限控制时,要再重复写一遍权限控制的代码,因此我们很有必要将权限控制的功能开发成一个组件(可插拔)。

  组件其实就是一个包,将一个与功能相关的代码关联到一起,当其他项目要使用该功能时将组件导入即可,下面我们试着来将权限控制写成一个组件,以客户管理系统为例,利用权限控制组件,实现动态显示权限菜单的功能,目录结构如下:

    luffy_permission/
    ├── db.sqlite3
    ├── luffy_permission
    │   ├── __init__.py
    │   ├── settings.py
    │   ├── urls.py
    │   └── wsgi.py
    ├── manage.py
    ├── rbac            # 权限组件,便于以后应用到其他系统
    │   ├── __init__.py
    │   ├── admin.py
    │   ├── apps.py
    │   ├── models.py
    │   ├── tests.py
    │   └── views.py
    ├── templates
    └── web            # 客户管理业务
        ├── __init__.py
        ├── admin.py
        ├── apps.py
        ├── models.py
        ├── tests.py
    ├── urls.py
        └── views.py

  (1)rbac/models.py中代码(权限管理的模型类),如下:

from django.db import models

class User(models.Model):
    """
    用户表
    """
    name = models.CharField(verbose_name=‘用户名‘, max_length=32)
    password = models.CharField(verbose_name=‘密码‘, max_length=32)
    roles = models.ManyToManyField(verbose_name=‘拥有的所有角色‘, to=‘Role‘)

    def __str__(self):
        return self.name

class Role(models.Model):
    """
    角色表
    """
    title = models.CharField(verbose_name=‘角色名称‘, max_length=32)
    permissions = models.ManyToManyField(verbose_name=‘拥有的所有权限‘, to=‘Permission‘)
    def __str__(self):
        return self.title

class Permission(models.Model):
    """
    权限表
    """
    url = models.CharField(verbose_name=‘含正则的URL‘, max_length=32)
    title = models.CharField(verbose_name=‘标题‘, max_length=32)
    is_menu = models.BooleanField(verbose_name=‘是否是菜单‘, default=False)
    icon = models.CharField(verbose_name=‘图标‘, max_length=32, blank=True)
    def __str__(self):
        return self.title

  (2)web/models.py(客户管理业务逻辑的模型类),如下:

from django.db import models

class Customer(models.Model):
    """
    客户表
    """
    name = models.CharField(verbose_name=‘姓名‘, max_length=32)
    age = models.CharField(verbose_name=‘年龄‘, max_length=32)
    email = models.EmailField(verbose_name=‘邮箱‘, max_length=32)
    company = models.CharField(verbose_name=‘公司‘, max_length=32)
    def __str__(self):
        return self.name

class Payment(models.Model):
    """
    付费记录表
    """
    customer = models.ForeignKey(verbose_name=‘关联客户‘, to=‘Customer‘,on_delete=models.CASCADE)
    money = models.IntegerField(verbose_name=‘付费金额‘)
    create_time = models.DateTimeField(verbose_name=‘付费时间‘, auto_now_add=True)

  (3)以上客户管理系统中的URL和对应的功能有(将url与视图函数对应关系配置在web下的urls.py中,再由全局的urls.py做路由分发):

    客户管理:

      客户列表:/customer/list/

      添加客户:/customer/add/

      删除客户:/customer/del/(\d+)/

      修改客户:/customer/edit/(\d+)/

    账单管理:

      账单列表:/payment/list/

      添加账单:/payment/add/

      删除账单:/payment/del/(\d+)/

      修改账单:/payment/edit/(\d+)/

  (4)在权限组件表中录入相关信息:

    录入权限列表,创建角色(并为角色分配权限),创建用户(并为用户分配角色);

    这样用户登录时,就可以根据当前登录用户找到其所有权限再将权限信息放入session,以后每次访问时候需要先去session检查是否有权访问。

    上篇中我们已经完成这些,接下来将权限和项目解耦并且完成动态显示权限菜单功能。

回到顶部

二、动态显示权限菜单(单级菜单)

  准备工作已经就绪,再按照以下步骤进行:

1、登录页面

  在web目录下的urls.py中新增加一个url与登录视图函数的对应关系:

    登录:/login/

  在web目录下的views.py中创建登录的视图函数,代码如下;

from rbac.service.setsession import initial_session
def login(request):
    if request.method == ‘POST‘:
        user = request.POST.get(‘user‘)
        pwd = request.POST.get(‘pwd‘)
        user_obj = User.objects.filter(name=user, password=pwd).first()
        if user_obj:
            request.session[‘user_id‘] = user_obj.pk  # 用户id注入session
            # 将权限列表和权限菜单列表注入session
            initial_session(user_obj, request)
            return redirect(‘/customer/list/‘)

    return render(request, ‘login.html‘)

  然后在web目录下创建templates文件夹,并在其中创建login.html;

2、在rbac目录下创建service文件夹,并在其中创建middlewares.py和setsession.py

  middlewares.py(利用中间件做用户登录和判断权限,中间件要加入全局settings中),代码如下:

from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import redirect, HttpResponse
import re
class PermissionMiddleWare(MiddlewareMixin):
    def process_request(self, request):
        # 设置白名单放行
        for reg in ["/login/", "/admin/*"]:
            ret = re.search(reg, request.path)
            if ret:
                return None

        # 检验是否登录
        user_id = request.session.get(‘user_id‘)
        if not user_id:
            return redirect(‘/login/‘)

        # 检验权限
        permission_list = request.session.get(‘permission_list‘)
        for reg in permission_list:
            reg = ‘^%s$‘ % reg
            ret = re.search(reg, request.path)
            if ret:
                return None
        return HttpResponse(‘无权访问‘)

  setsession.py(将当前登录人的所有权限注入session中),代码如下:

def initial_session(user_obj, request):
    """
    将当前登录人的所有权限列表和所有菜单权限列表注入session
    :param user_obj: 当前登录用户对象
    :param request: 请求对象HttpRequest
    """
    # 查询当前登录人的所有权限列表
    ret = Role.objects.filter(user=user_obj).values(‘permissions__url‘,
                                            ‘permissions__title‘,
                                           ‘permissions__icon‘,
                                      ‘permissions__is_menu‘).distinct()
    permission_list = []
    permission_menu_list = []
    for item in ret:
        permission_list.append(item[‘permissions__url‘])
        if item[‘permissions__is_menu‘]:
            permission_menu_list.append({
                ‘url‘: item[‘permissions__url‘],
                ‘title‘: item[‘permissions__title‘],
                ‘icon‘: item[‘permissions__icon‘],
            })
    print(‘权限列表‘, permission_list)
    print(‘菜单权限列表‘, permission_menu_list)
    # 将当前登录人的权限列表注入session中
    request.session[‘permission_list‘] = permission_list
    # 将当前登录人的菜单权限列表注入session中
    request.session[‘permission_menu_list‘] = permission_menu_list

3、客户列表和账单列表属于菜单列表,我们需要渲染到左侧菜单中,根据用户权限判断是否显示,而客户列表和账单列表的左侧菜单是在公共的模板base.html中定义的,我们只看左侧菜单的部分,如下代码:

<div class="menu-body">
    <div class="static-menu">
       {% for item in permission_menu_list %}
          <a href="{{ item.url }}">
             <span class="icon-wrap"><i class="fa {{ item.icon }}"></i></span>{{ item.title }}
          </a>
       {% endfor %}
    </div>
</div>

  至此,我们已经实现了动态渲染标签的功能,也就是用户拥有的菜单权限会在页面左侧菜单中显示,没有权限的则不显示。但是还有一个小问题,鼠标滑过相应菜单有一个样式,但是鼠标移走样式消失。如何解决呢?你可能会想到在客户列表和账单列表对应的视图函数中分别加上这样一段逻辑代码:即当前视图函数对应url与用户请求url相同时,给从当前用户session中取出的当前用户菜单全列列表中的字典添加一个键值对(class:active)再传给返回的页面,这种方法是可以满足需求,但同时也存在代码重复的问题,就是在不同的视图函数中重复写了一样的逻辑代码。下面介绍的自定义标签的扩展就可以完美解决。

4、自定义标签扩展功能实现点击菜单增加相应active类名:

  1)保证全局settings.py中的INSTALLED_APPS配置了当前应用rbac;

  2)在rbac目录下创建templatetags模块(文件夹);

  3)在templatetags中创建任意的.py文件(如:my_tags.py),然后就可以在里边写自定义的标签扩展的函数了,如下:

from django import template
register = template.Library()
@register.inclusion_tag("menu.html")  # django会自动去templates中寻找
def get_menu_styles(request):
        permission_menu_list = request.session.get("permission_menu_list")
        for item in permission_menu_list:
            if re.search("^{}$".format(item["url"]), request.path):
                item["class"] = "active"
        return {"permission_menu_list": permission_menu_list} # 返回给menu.html

  4)在rbac目录下创建templates文件夹,并在其中创建任意的.html文件(如:menu.html),该页面存放左侧菜单部分,变量可以由自定义标签函数返回,内容如下:

<div class="static-menu">
    {% for item in permission_menu_list %}
        <a href="{{ item.url }}" class="{{ item.class }}">      <span class="icon-wrap"><i class="fa {{ item.icon }}"></i></span> {{ item.title }}
        </a>
    {% endfor %}
</div>

  5)在模板页面base.html中显示菜单的位置先导入之前创建的my_tags.py文件,再调用自定义标签函数,如下:

  <div class="menu-body">
    {% load my_tags %}
    {% get_menu_styles request %}    # 依次写函数名和参数,空格隔开
  </div>

  分析:当调用自定义标签函数get_menu_styles时,就会执行my_tags.py中相应函数并将返回值返回给函数对应装饰器中定义的menu.html页面,渲染成html页面后再将渲染结果返回到调用get_menu_styles的页面,这样做的好处是,当多个视图函数需要返回给浏览器同一个模板页面且都需要给这个模板页面的相同地方传递变量且该变量是由相同的业务逻辑产生,这个时候我们可以利用自定义标签函数,变量统一由自定义标签函数返回给一个页面(这个页面是将模板页面中共同使用这个变量的代码块提取出来所构成,如例中的menu.html),再在模板页面中调用该函数,避免了在视图函数中重复写相同的业务逻辑代码。这也是过滤器(包括django已有的和自定义的过滤器)和自定义标签的存在意义,即提高代码的复用性。

回到顶部

三、补充知识点

1、admin补充 - list_editable和search_fields

  我们之前了解过Django提供的admin,其实admin的功能相当强大,我们目前了解的仅仅是九牛一毛,上篇中我们学习了如何在admin中自定义显示样式,今天我们再学习两个:list_editable和search_fields。还是以权限控制中的权限表为例:

  上篇中我们为Permission表创建了三个字段(id、url、title),今天又加了两个(is_menu、icon),当我们在admin为permission表按如下定义时:

# 自定义类,类名自己定,但必须继承ModelAdmin
class PermissionConfig(admin.ModelAdmin):
  list_display = [‘pk‘, ‘title‘, ‘url‘, ‘is_menu‘, ‘icon‘]
  list_editable = [‘url‘, ‘is_menu‘, ‘icon‘]
  search_fields = [‘title‘]
  ordering = [‘pk‘]  # 按照主键从低到高

admin.site.register(Permission, PermissionConfig)

  效果如图:

  总结:

    1)list_editable   字段在展示的同时可以编辑;

    2)search_fields   显示按某字段搜索的功能;

2、自定义标签和过滤器

  (1)首先保证settings.py中的INSTALLED_APPS配置了当前app,否则django无法找到自定义的标签和过滤器;

  (2)在app中创建templatetags模块(也就是包,包的名字只能是templatetags);

  (3)在templatetags中创建任意的.py文件(如:my_tags.py),然后就可以在里边写自定义的标签和过滤器了,如下:

from django.utils.safestring import mark_safe
from django import template
register = template.Library()   # register的名字是固定的,不可改变

@register.filter               # 自定义过滤器(filter)
def filter_multi(v1, v2):
  return v1 * v2

@register.simple_tag            # 自定义标签(simple_tag)
def simple.tag_multi(v1, v2):
  return v1 * v2

@register.simple_tag           # 自定义标签(simple_tag)
def my_input(id, arg):
  result = "<input type=‘text‘ id=‘%s‘ class=‘%s‘ />" % (id, arg)
  return mark_safe(result)

  (4)在使用自定义simple_tag和filter的html文件中先导入之前创建的my_tags.py,再使用,如下:

{% load my_tags %}   # 导入

# num=12
{{ num|filter_multi:2 }}  # 24

{{ num|filter_multi:"[22,333,4444]" }}

{% simple_tag_multi 2 5 %}    # 参数不限,但不能放在if、for语句中
{% simple_tag_multi num 5 %}

  总结:

    1)自定义过滤器的函数对参数有限制,只能是一个或者两个,当你想传超过两个的参数时,只能自己想办法,比如可以把参数放到列表中再传入,而自定义标签的函数参数可以有任意多个;

    2)django在查找自定义标签和过滤器文件时,会依次查找INSTALLED_APPS中已配置的app下的templatetags模块中与load后同名的py文件,若两个app中的templatetags都有相同的py文件且文件中定义的同名的过滤器或者标签函数,那么后者会覆盖前者,因此尽量避免py文件同名。

    3)自定义simple_tag不可以放在if、for语句中,而filter可以用在if等语句后,如下:

{% if num|filter_multi:30 > 100 %}
    {{ num|filter_multi:30 }}
{% endif %}

3、Font Awesome - 一套绝佳的图标字体库和CSS框架

官网:http://fontawesome.dashgame.com/

原文地址:https://www.cnblogs.com/kenD/p/10651500.html

时间: 2024-10-03 22:29:03

权限(二)的相关文章

包和访问权限(二)

1. java的访问权限 2. 软件包的导入 1. java的访问权限 <1> public  公共权限 既可修饰类  又可修饰成员变量和成员函数 不在同一个包, 对其访问时必须是公共类 1 package mirror; 2 3 class Person{ 4 public String name; 5 public void introduce(){ 6 System.out.println("name"); 7 } 8 } package Mirror; class

Gitlab管理用户、组、权限(二)

一. 保护主要分支 保护某个分支,不让项目中其它的成员进行改变里面的内容,和不允许他们进行和保护的分支进行合并.当某个分支设置为保护的时候,那么拥有开者者权限或以下的权限都会受到影响. 执行步骤: 1. 使用管理员进入到Gitlab服务器. 2. 切换到你想要保护的项目. 3. 点击[settings] 4. 点击Protection branches. 二. 配置项目的可见性 1. 使用管理员进入到Gitlab服务器. 2. 切换到你想要保护的项目. 3. 点击[settings] 4. 点击

linux特殊权限(二)

在上文基本权限中讲述了rwx的作用.但是或许有人在权限蓝栏看到了一个s符号,这个就是特殊权限. 文件的特殊权限包括:SUID.SGID.SBIT. 1.SUID,借出程序所有者的权限 [email protected]:/home$ ll /usr/bin/passwd -rwsr-xr-x 1 root root 54256 3月 29 2016 /usr/bin/passwd* #拥有SUID权限,第一组rwx中的x改为s passwd命令为修改用户的密码,根据权限可以得知同一用户组或其他人

Android 6.0 应用权限(二) -- 与系统权限一起工作(Working with System Permissions) 使用MarkDown重新整理了排版

为了保护系统集成度和用户的隐私,Android在一个受限访问的沙箱中运行每个程序.如果这个应用想要使用它的沙箱外的其他资源或信息,就必须显性地请求权限.依赖于应用请求的权限类型,系统可能会自动地授权或者通过询问用户来授权 本节向你展示如何为你的应用声明和授予权限. Declare Permissions(声明权限) 每个Android应用都运行在一个受限访问的沙箱中..如果这个应用想要使用它的沙箱外的其他资源或信息,就需要请求正确的权限.你可以通过在App Manifest中列出这些权限来声明你

文件权限二(特殊权限、隐藏属性、文件访问控制列表、切换用户方式)

文件特殊权限:SUID:将文件执行者临时获取所以者权限:U=user 用户命令格式:chmod u + s 文件SGID:临时获取所有组权限:G=group 组命令格式:chmod g + s 文件SBIT:只能自己删除自己的文件,因为SBIT保护位将文件保护起来命令格式:chmod o + t 文件 注意:文件/目录用数字4位表示,其中第一位表示是否有特殊权限,第2-4位表示一般权限或特殊权限转换后的值.通过用数字的方式表示权限可以使设置用户(或文件.目录)权限时,更加方便,不用去记9位的rw

20150404--RBAC+管路员权限-01

目录 一.权限管理设置 1 1.删除权限 1 二.角色管理 2 1.添加角色, 2 2.角色列表 5 3.角色删除 6 三.管理员管理 6 2.管理员列表, 9 3.修改管理员 9 五.模型管理 12 六.栏目管理 13 七.内容管理 13 一.权限管理设置 1.删除权限 在删除权限时,如果有子权限则不能删除.在删除之前要判断一下是否有子权限. (1)在模型中定义一个方法,用于验证是否子权限. (2)在privilege控制器 里面添加一个delete的方法.用于删除权限 二.角色管理 1.添加

Linux文件权限学习总结

一.用户对文件或目录都有哪些权限? 四种:读.写.执行.没有权限 二.如何表示这四种权限? 如果用十进制数字表示,分别为:4.2.1.0:如果用字符表示,分别为:r.w.x.-.个人觉得,使用chmod命令更改文件或目录权限时,数字表示法显然比字符表示法简洁明快 三.对于文件和目录而言,这四种权限是否具有同样的含义? 并非如此,如下所示: ---------------------------------------------------------------------------    

Windows server 2008的文件和文件权限

工作任务描述 HT公司准备搭建一台文件服务器.公司当前的情况是有销售,财务,信息三个部门.每个部门有几名员工,其中一名是其部门经理 部门 中文名 英文名 Manager 凯文 Kevin Information Department (信息部) 田田 Andrew 麦麦 Michael 小刚 Ethan 麦兜 Mike 大卫 David Financial Department (财务部) 克克 Noah 文文 Emily 小迪 Eadison Sales Department (销售部) 思玲

linux系统中文件的特殊权限

在上篇博客中叙述linux系统中文件的基本属性,见http://vinsent.blog.51cto.com/13116656/1951574,这篇给大家带来linux系统文件的特殊权限,包括SUID.SGID.Sticky(粘滞位). 一.安全上下文 安全上下文指的是一类定义某个进程允许做什么的许可和权限的集合.安全上下文的概念范围很广范,权限.特权.访问令牌.完整性等级等等都包含在其中.这里只简要说说linux系统对文件的安全控制: (1)进程有属主和属组,文件有属主和属组      (2)