Django之权限管理插件

参考:https://www.cnblogs.com/alex3714/articles/6661911.html

   http://www.cnblogs.com/wupeiqi/articles/6229414.html

1.      什么是权限?

权限就是对软件系统中各种资源的访问和操作的控制!

2.      什么是资源?

在软件系统中,数据库、内存、硬盘里数据都是资源,资源就是数据!

3.      动作

资源本身是静态的,必须通过合适的动作对其进行访问和操作,我们说要控制权限,其实本质上是要对访问软件中各种数据资源的动作进行控制

4.      为什么需要权限管理系统?

不同职责的工作人员对于系统操作的权限应该是不同的,不同的用户也无法窥探其他用户的信息,因此权限管理在业务中是必不可少的。

角色(组):可以对“组”进行权限分配,将权限一致的人员编入同一组,然后对该组进行权限分配。

可扩展的:它应该可以加入到任何带有权限管理功能的系统中。就像是组件一样的可以被不断的重用,而不是每开发一套管理系统,就要针对权限管理部分进行重新开发。

5.      权限管理系统的两种模式?

在web开发中只有两种架构,一种是B\S架构(浏览器服务端架构),另一种是C\S架构(客户端服务器端架构),B/S系统中的权限比C/S中的更显的重要,C/S系统因为具有特殊的客户端,所以访问用户的权限检测可以通过客户端实现或通过客户端+服务器检测实现,而B/S中,浏览器是每一台计算机都已具备的,如果不建立一个完整的权限检测,那么一个“非法用户”很可能就能通过浏览器轻易访问到B/S系统中的所有功能。因此B/S业务系统都需要有一个或多个权限系统来实现访问权限检测,让经过授权的用户可以正常合法的使用已授权功能,而对那些未经授权的“非法用户”将会将他们彻底的“拒之门外”。下面就让我们一起了解一下如何设计可以满足大部分B/S系统中对用户功能权限控制的权限系统。

6.      权限管理系统的开发流程

权限条目的定义

权限条目与用户的关联

权限组件与应用的结合

7.      自定义权限

一个动作 = 一条权限 = 一个url + 一种请求方法(get/post/put...) + 若干个请求参数

请求参数是为了细化权限管理,比如我只允许你操作几行数据,或者哪一类的数据

perm_dict={
    ‘crm_table_index‘:[‘table_index‘,‘GET‘,[],{},],  #可以查看CRM APP里所有数据库表
    ‘crm_table_list‘:[‘table_list‘,‘GET‘,[],{}],    #可以查看每张表里所有的数据
    ‘crm_table_list_view‘:[‘table_change‘,‘GET‘,[],{}],#可以访问表里每条数据的修改页
    ‘crm_table_list_change‘:[‘table_change‘,‘POST‘,[],{}], #可以对表里的每条数据进行修改
    }

字典里的key是权限名,我们需要用过这些权限名来跟用户进行关联

后面values列表里第一个值如‘table_index‘是django中的url_name,在这里必须相对的url name, 而不是绝对url路径,因为考虑到django url正则匹配的问题,搞绝对路径,不好控制。

values里第2个值是http请求方法

values里第3个[]是要求这个请求中必须带有某些参数,但不限定对数的值是什么

values里的第4个{}是要求这个请求中必须带有某些参数,并且限定所带的参数必须等于特定的值

8.      权限条目与用户的关联

我们用两种形式来实现与用户的关联,第一种形式可以使用django自带的权限管理系统,另一种是自定义数据表来实现。

了解一下内置的权限都实现了哪些功能和创建了哪些对象,对我们自定义实现也是很有帮助的

8.1 django自带权限管理

Django内置的权限系统包括以下三个部分:

用户(Users)

许可(Permissions):用来定义一个用户(user)是否能够做某项任务(task)

组(Groups):一种可以批量分配许可到多个用户的通用方式

首先最重要的开始就是User模型

User模型对应于一个用户,一个帐户,位于‘django.contrib.auth.models‘模块中。

User对象有两个多对多的属性分别是:groups和user_permissions

User对象的Manager,UserManager:

和其他的模型一样,User模型类的objects属性也是一个Manager对象,但是User的Manager对象是自定义的,增加了一些方法:

create_user(username,email=None,password=None)

该方法创建保存一个is_active=True的User对象并返回。username不能够为空,否则抛出ValueError异常。email和password都是可选的。email的domain部分会被自动转变为小写。password如果没有提供,则User对象的set_unusable_password()方法将会被调用。

make_random_password(length=10,allowed_chars=‘abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789‘)

该方法返回一个给定长度和允许字符集的密码。其中默认的allowed_chars有一些字符没有,比如i,l等等。

User对象的属性:

  username:字符串类型。必填。30个字符以内。

  first_name:字符串类型。可选。30个字符以内。

  last_name:字符串类型。可选。30个字符以内。

  email:可选。

  password:明文密码的hash或者是某种元数据。该属性不应该直接赋值明文密码,而应该通过set_password()方法进行赋值,在后面有详细说明TODO。

  is_staff:Boolean类型。用这个来判断是否用户可以登录进入admin site。

  is_active:Boolean类型。用来判断该用户是否是可用激活状态。在删除一个帐户的时候,可以选择将这个属性置为False,而不是真正删除。这样如果应用有外键引用到这个用户,外键就不会被破坏。

  is_superuser:Boolean类型。该属性用来表示该用户拥有所有的许可,而无需明确的赋予给他。

  last_login:datetime类型。最近一次登陆时间。

date_joined:datetime类型。创建时间。

User对象方法

         is_anonymous():

永远返回False.用来将User对象和AnonymousUser(未登录的匿名用户)对象作区分用的识别方法。通常,最好用is_authenticated()方法。

        is_authenticated():

      永远返回True。该方法不代表该用户有任何的许可,也不代表该用户是active的,而只是表明该用户提供了正确的username和password。

        get_full_name():

      返回一个字符串,是first_name和last_name中间加一个空格组成。

        set_password(raw_password):

      调用该方法时候传入一个明文密码,该方法会进行hash转换。该方法调用之后并不会保存User对象。

        check_password(raw_password):

      如果传入的明文密码是正确的返回True。该方法和set_password是一对,也会考虑hash转换。

        set_unusable_password():

      将用户设置为没有密码的状态。调用该方法后,check_password()方法将会永远返回false。但是如果,调用set_password()方法重新设置密码后,该方法将会失效,has_usable_password()也会返回True。

        has_usable_password():

      在调用set_unusable_password()方法之后,该方法返回False,正常情况下返回True。

        get_group_permissions(obj=None):

      返回该用户通过组所拥有的许可(字符串列表每一个代表一个许可)。obj如果指定,将会返回关于该对象的许可,而不是模型。

        get_all_permissions(obj=None):

      返回该用户所拥有的所有的许可,包括通过组的和通过用户赋予的许可。

        has_perm(perm,obj=None):

     如果用户有传入的perm,则返回True。perm可以是一个格式为:‘<app label>.<permission codename>‘的字符串。如果User对象为inactive,该方法永远返回False。和前面一样,如果传入obj,则判断该用户对于这个对象是否有这个许可。

        has_perms(perm_list,obj=None):

      和has_perm一样,不同的地方是第一个参数是一个perm列表,只有用户拥有传入的每一个perm,返回值才是True。

        has_module_perms(package_name):

      传入的是Django app label,按照‘<app label>.<permission codename>‘格式。当用户拥有该app label下面所有的perm时,返回值为True。如果用户为inactive,返回值永远为False。

        email_user(subject,message,from_email=None):

      发送一封邮件给这个用户,依靠的当然是该用户的email属性。如果from_email不提供的话,Django会使用settings中的DEFAULT_FROM_EMAIL发送。

        get_profile():

      返回一个和Site相关的profile对象,用来存储额外的用户信息。

Login Logout

得到了用户对象,接下来只需要对用户密码进行验证跳转到指定页面即可,django为我们提供了两个很好用的函数:

authenticate验证

使用命令行可以进行测试,authenticate(username,password)函数需要两个参数username,password,如果校验通过则返回User对象,如果校验不通过返回None

login_required装饰器

django.contrib.auth.decorators.login_required([redirect_field_name=REDIRECT_FIELD_NAME,login_url=None])

login_required方法接受两个参数:

redirect_field_name:默认值是next。用来定义登陆成功之后的跳回之前访问界面的url。

login_url:默认值是settings.LOGIN_URL。用来指定登陆界面的url。如果不传入改参数,就需要确保settings.LOGIN_URL的值是正确设置的。

许可(Permission) 和 用户组(Group)

自己定义一些许可,就是在Model类的meta属性中添加permissions定义。比方说,创建了一个模型类叫做Discussion,我们可以创建几个权限来对这个模型的权限许可进行控制,控制某些人可以发起讨论、发起回复,关闭讨论:

在model.py的任何一张表下增加class Meta:

class Discussion(models.Model):

class Meta:

permissions = (

("open_discussion", "Can create a discussion"),

("reply_discussion", "Can reply discussion"),

("close_discussion", "Can remove a discussion by setting its status as closed"),

)

执行manage.py syncdb就会把增加的权限信息录入到后台数据库。

通过某一个user的user_permissions属性,permission_1为auth_permission表中的id值:

user.user_permissions.add(permission_1, permission_2, ...)

删除权限:

user.user_permissions.remove(permission_1, permission_2, ...)

通过user的一个组,然后通过group的permissions属性:

group.permissions.add(permission_1, permission_2, ...)

我们要判断一个用户是否有发讨论的权限,我们可以用下面的代码:

user.has_perm(‘school.open_discussion‘)

8.2  如何利用django内置权限来管理权限呢?

答案很简单,我们只需要把写好的权限放在django自动生成的数据表即可

在model.py的任何一张表下增加class Meta:

再用几行语句把权限添加到user对象中即可,django会自动帮我们把权限和用户管理起来了

user.user_permissions.add(permission_1, permission_2, ...)

或者直接在admin中操作更为快捷

以上操作仅仅是将用户权限名和用户关联起来了,但是具体每个权限名代表什么我们还不知道,因此,我们需要自定义一个字典来存储用户权限名和每个url,方法,参数的对应关系。

每次取到权限名再去我们的字典中取匹配

perm_dic = {
    # auth_permissions表权限    url的别名    请求方法  携带参数?name=‘2GO‘
    ‘view_customer_list‘: [‘customer_list‘,‘GET‘,[]],
    ‘view_customer_info‘: [‘customer_detail‘,‘GET‘,[]],
    ‘edit_own_customer_info‘: [‘customer_detail‘,‘POST‘,[‘qq‘,‘nam‘]],
}

    因为我们可能不会使用auth中权限,所以没有必要去取回auth的所有权限名,只需要从我们的perm_dic定义好就行,每个url的别名都要对应好所需的views,所以url的名字要取得有意义

8.3 权限组件与应用的结合

    我们希望我们的权限组件是通用的,可插拔的,它一定要与具体的业务代码分离,以后可以轻松把这个组件移植到其它的项目里去,因此这里我们采用装饰器的模式,把权限的检查、控制封装在一个装饰器函数里,想对哪个Views进行权限控制,就只需要在这个views上加上装饰器就可以了。

@check_permission
def table_change(request,app_name,table_name,obj_id):
    .....
  1. 拿到用户请求的url+请求方法+参数到我们的的perm_dic里去一一匹配
  2. 当匹配到了对应的权限条目后,就拿着这个条目所对应的权限名,和当前的用户, 调用request.user.has_perm(权限名)
  3. 如果request.user.has_perm(权限名)返回为True,就认为该用户有权限 ,直接放行,否则,则返回403页面!

下面的函数用到了url的映射函数,先简单的介绍一下

from django.urls import resolve, reverse
# resolve,reverse用来对url和name,func进行映射操作
reverse(viewname, urlconf=None, args=None, kwargs=None, current_app=None)
# 可以将name映射到url,参数viewname可以是任何可以调用的对象或者是url_name(在url定义的)
# 没有匹配会抛出异常raises a NoReverseMatch exceptio
# 需要传参数的时候可以在后面加,但是args和kwargs只能传递一个
# 比如在网页重定向的时候可以使用
# return HttpResponseRedirect(reverse(‘arch-summary‘, args=[1945]))

resolve(path, urlconf=None)
# 匹配成功后返回ResolverMatch对象,允许我们对解析后url的一些操作,它的属性有
# func 匹配到的函数 view_name函数对应的名称
# args,kwargs 传递的参数
# url_name?
# app_name?
# 匹配失败返回404页面

进入正题

from django.urls import resolve, reversefrom django.shortcuts import render,redirect,HttpResponse
from kingadmin.permission_list import perm_dic
from django.conf import settings

def perm_check(*args,**kwargs):

    request = args[0]
    resolve_url_obj = resolve(request.path)
    current_url_name = resolve_url_obj.url_name  # 当前url的url_name
    print(‘---perm:‘,request.user,request.user.is_authenticated(),current_url_name)
    #match_flag = False
    match_key = None
    if request.user.is_authenticated() is False:
         return redirect(settings.LOGIN_URL)

    for permission_key,permission_val in  perm_dic.items():

        per_url_name = permission_val[0]
        per_method  = permission_val[1]
        perm_args = permission_val[2]
        perm_kwargs = permission_val[3]

        if per_url_name == current_url_name: #matches current request url
            if per_method == request.method: #matches request method
                # if not  perm_args: #if no args defined in perm dic, then set this request to passed perm 

                #逐个匹配参数,看每个参数时候都能对应的上。
                args_matched = False #for args only
                for item in perm_args:
                    request_method_func = getattr(request,per_method)    # 相当于request.get 或者request.post
                    if request_method_func.get(item,None):# request字典中有此参数
                        args_matched = True
                    else:
                        print("arg not match......")
                        args_matched = False
                        break  # 有一个参数不能匹配成功,则判定为假,退出该循环。
                else:        #  for...else... 空列表
                    args_matched = True

#匹配有特定值的参数
                kwargs_matched = False
                for k,v in perm_kwargs.items():
                    request_method_func = getattr(request, per_method)
                    arg_val = request_method_func.get(k, None)  # request字典中有此参数
                    print("perm kwargs check:",arg_val,type(arg_val),v,type(v))
                    if arg_val == str(v): #匹配上了特定的参数 及对应的 参数值, 比如,需要request 对象里必须有一个叫 user_id=3的参数
                        kwargs_matched = True
                    else:
                        kwargs_matched = False
                        break # 有一个参数不能匹配成功,则判定为假,退出该循环。
                else:
                    kwargs_matched = True

                match_results = [args_matched,kwargs_matched]
                print("--->match_results ", match_results)
                if all(match_results): #都匹配上了
                    match_key = permission_key
                    break

    if all(match_results):
        app_name, *per_name = match_key.split(‘_‘)
        print("--->matched ",match_results,match_key)
        print(app_name, *per_name)
        perm_obj = ‘%s.%s‘ % (app_name,match_key)
        print("perm str:",perm_obj)
        if request.user.has_perm(perm_obj):
            print(‘当前用户有此权限‘)
            return True
        else:
            print(‘当前用户没有该权限‘)
            return False

    else:
        print("未匹配到权限项,当前用户无权限")

def check_permission(func):
    def inner(*args,**kwargs):
        if not perm_check(*args,**kwargs):
            request = args[0]
            return render(request,‘kingadmin/page_403.html‘)
        return func(*args,**kwargs)
    return  inner

权限检查代码

9 自定义权限管理组件

  在使用django内置的权限管理操作,我们省略了最为关键的一个步骤,表结构的设计,可以说权限表结构的设计才是权限管理系统的灵魂,所有的一切都是服务于这个表结构设计的。

表结构设计如下:

from django.db import models

# Create your models here.

class User(models.Model):
    username = models.CharField(max_length=32)
    password = models.CharField(max_length=64)
    # m = models.ManyToManyField("Role")

    class Meta:
        verbose_name_plural = ‘用户表‘

    def __str__(self):
        return self.username

# 角色表是集合了某些权限的一类人
class Role(models.Model):
    caption = models.CharField(max_length=32)

    class Meta:
        verbose_name_plural = ‘角色表‘

    def __str__(self):
        return self.caption

# 用户和角色表示多对多关系的,一个用户可以是多个角色,一个角色也可以对应多个用户,我们自建第三张多对多表便于查看
class User2Role(models.Model):
    u = models.ForeignKey(User, on_delete=True)
    r = models.ForeignKey(Role, on_delete=True)

    class Meta:
        verbose_name_plural = ‘用户分配角色‘

    def __str__(self):
        return "%s-%s" % (self.u.username,
                          self.r.caption,)

class Action(models.Model):
    # get 获取信息
    # post 创建用户
    # put 修改用户
    # delete 删除用户
    caption = models.CharField(max_length=50)
    code = models.CharField(max_length=50)

    class Meta:
        verbose_name_plural = ‘操作表‘

    def __str__(self):
        return self.caption

# menu自关联menu,因为没法确定menu的层级关系到底是多少层
class Menu(models.Model):
    caption = models.CharField(max_length=50)
    parent = models.ForeignKey(‘self‘, related_name=‘p‘, null=True, blank=True, on_delete=True)

    class Meta:
        verbose_name_plural = ‘ 菜单 ‘

    def __str__(self):
        return self.caption

class Permission(models.Model):
    # 一个权限就是一个url加上一个动作
    # 127.0.0.1:8000/user?t=ger/post/put/delete
    caption = models.CharField(max_length=50)
    url = models.CharField(max_length=100)
    # 建立外键关系是向上查找的,如果在permission后面定义的menu就会显示差找不到
    menu = models.ForeignKey(Menu, null=True, blank=True, on_delete=True)

    class Meta:
        verbose_name_plural = ‘URL表‘

    def __str__(self):
        return "%s-%s" % (self.caption, self.url,)

class Permission2Action(models.Model):
    # 真正的权限表
    p = models.ForeignKey(Permission, on_delete=True)
    a = models.ForeignKey(Action, on_delete=True)

    class Meta:
        verbose_name_plural = ‘权限表‘

    def __str__(self):
        return "%s-%s:-%s?t=%s" % (self.p.caption, self.a.caption, self.p.url, self.a.code,)

class Permission2Action2Role(models.Model):
    p2a = models.ForeignKey(Permission2Action, on_delete=True)
    r = models.ForeignKey(Role, on_delete=True)

    class Meta:
        verbose_name_plural = ‘角色分配权限‘

    def __str__(self):
        return "%s==>%s" % (self.r.caption, self.p2a,)

class Task(models.Model):
    caption = models.CharField(max_length=50)

    class Meta:
        permissions = (
            ("view_task", "Can see available tasks"),
            ("change_task_status", "Can change the status of tasks"),
            ("close_task", "Can remove a task by setting its status as closed"),
        )

将权限管理封装成一个类

from django.shortcuts import render, HttpResponse, redirect
from rabc import models
import re

# Create your views here.

class MenuHelper(object):
    def __init__(self, request, username):
        # 当前请求的request对象
        self.request = request
        # 当前用户名
        self.username = username
        # 获取当前URL,用来匹配该显示的权限和菜单
        self.current_url = request.path_info
        # 获取当前用户的所有权限
        self.permission2action_dict = None
        # 获取在菜单中显示的权限
        self.menu_leaf_list = None
        # 获取所有菜单
        self.menu_list = None

        # 用来获取或者保存在session中的数据,以免在数据库重复的取数据,无法获得实时更新的数据
        self.session_data()

    def session_data(self):
        permission_dict = self.request.session.get(‘permission_info‘)
        if permission_dict:
            self.permission2action_dict = permission_dict[‘permission2action_dict‘]
            self.menu_leaf_list = permission_dict[‘menu_leaf_list‘]
            self.menu_list = permission_dict[‘menu_list‘]
        else:
            # 获取当前用户的角色列表
            role_list = models.Role.objects.filter(user2role__u__username=self.username)

            # 获取当前用户的权限列表(URL+Action)
            # v = [
            #     {‘url‘:‘/index.html‘,‘code‘:‘GET‘},
            #     {‘url‘:‘/index.html‘,‘code‘:‘POST‘},
            #     {‘url‘:‘/order.html‘,‘code‘:‘PUT‘},
            #     {‘url‘:‘/order.html‘,‘code‘:‘GET‘},
            # ]
            # v = {
            #     ‘/index.html‘:[‘GET‘]
            # }

            # 方式一:
            # manytomany系统建表
            # role_list = models_obj.m.all()

            # 方式二 自建多对多表查询
            # user2role_list = models.User2Role.objects.filter(u = user_obj)

            # 方式三 角色表反向查询
            # role_list = models.Role.objects.filter(user2role__u=user_obj)

            # 方式四 连续跨表
            # role_list = models.Role.objects.filter(user2role__u__username=username)

            # 将重复的权限去重,并将权限放到session中,缺点:无法获取实时的权限信息
            # 或者从数据库中获取权限,缺点:如果每次都从数据库获取权限,需要每次操作数据库
            permission2action_list = models.Permission2Action.objects.                 filter(permission2action2role__r__in=role_list).                 values(‘p__url‘, ‘a__code‘).distinct()

            permission2action_dict = {}
            for item in permission2action_list:
                if item[‘p__url‘] in permission2action_dict:
                    permission2action_dict[item[‘p__url‘]].append(item[‘a__code‘])
                else:
                    permission2action_dict[item[‘p__url‘]] = [item[‘a__code‘], ]

            # 获取菜单的叶子节点,即:菜单的最后一层应该显示的权限
            # 叶子节点权限menu_leaf_list,最终可以跳转的权限<a>标签,从属的父节点仅仅是可以展开
            menu_leaf_list = list(models.Permission2Action.objects.filter(permission2action2role__r__in=role_list).
                                  exclude(p__menu__isnull=True).values(‘p_id‘, ‘p__url‘, ‘p__caption‘, ‘p__menu‘).
                                  distinct())

            # 获取所有的菜单列表
            menu_list = list(models.Menu.objects.values(‘id‘, ‘caption‘, ‘parent_id‘))

            self.request.session[‘permission_info‘] = {
                ‘permission2action_dict‘: permission2action_dict,
                ‘menu_leaf_list‘: menu_leaf_list,
                ‘menu_list‘: menu_list,
            }

            # self.permission2action_list = permission2action_list
            # self.menu_leaf_list = menu_leaf_list
            # self.menu_list = menu_list

    # 处理叶子节点,将叶子节点挂靠到菜单上,并返回应该现实的树形字典
    def menu_data_list(self):
        menu_leaf_dict = {}
        open_leaf_parent_id = None

        # 归并所有的叶子节点
        for item in self.menu_leaf_list:
            item = {
                ‘id‘: item[‘p_id‘],
                ‘url‘: item[‘p__url‘],
                ‘caption‘: item[‘p__caption‘],
                ‘parent_id‘: item[‘p__menu‘],
                ‘child‘: [],
                ‘status‘: True,  # 是否显示,叶子节点肯定是显示的
                ‘open‘: False  # 是否展开,只有拥有叶子节点的父节点才能展开,而且匹配到当前的url才需要展开
            }
            if item[‘parent_id‘] in menu_leaf_dict:
                menu_leaf_dict[item[‘parent_id‘]].append(item)
            else:
                menu_leaf_dict[item[‘parent_id‘]] = [item, ]
            if re.match(item[‘url‘], self.current_url):
                item[‘open‘] = True
                open_leaf_parent_id = item[‘parent_id‘]

        # 获取所有菜单字典
        menu_dict = {}
        for item in self.menu_list:
            item[‘child‘] = []
            item[‘status‘] = False
            item[‘open‘] = False
            menu_dict[item[‘id‘]] = item

        # 将叶子节点添加到菜单中
        # 字典中取key-value最好加items,因为如果items是其他复杂结构,没法切片和迭代
        for k, v in menu_leaf_dict.items():
            menu_dict[k][‘child‘] = v
            parent_id = k
            # 将后代中有叶子节点的菜单标记为status True
            while parent_id:
                menu_dict[parent_id][‘status‘] = True
                parent_id = menu_dict[parent_id][‘parent_id‘]

        # 将已经选中的菜单标记为【展开】
        while open_leaf_parent_id:
            menu_dict[open_leaf_parent_id][‘open‘] = True
            open_leaf_parent_id = menu_dict[open_leaf_parent_id][‘parent_id‘]

        # 生成树形结构数据
        result = []
        for row in menu_dict.values():
            if not row[‘parent_id‘]:
                result.append(row)
            else:
                menu_dict[row[‘parent_id‘]][‘child‘].append(row)
        return result

    def menu_content(self, child_list):
        response = ""
        tpl = """
            <div class="item %s">
                <div class="title">%s</div>
                <div class="content">%s</div>
            </div>
        """
        for row in child_list:
            if not row[‘status‘]:
                continue
            active = ""
            if row[‘open‘]:
                active = "active"
            # 有url的子项是权限项
            if ‘url‘ in row:
                response += "<a class=‘%s‘ href=‘%s‘>%s</a>" % (active, row[‘url‘], row[‘caption‘])
            else:
                title = row[‘caption‘]
                content = self.menu_content(row[‘child‘])
                response += tpl % (active, title, content)
        return response

    def menu_tree(self):
        response = ""
        tpl = """
        <div class="item %s">
            <div class="title">%s</div>
            <div class="content">%s</div>
        </div>
        """
        for row in self.menu_data_list():
            if not row[‘status‘]:
                continue
            active = ""
            if row[‘open‘]:
                active = "active"
            # 第一层第一个
            title = row[‘caption‘]
            # 第一层第一个的后代
            content = self.menu_content(row[‘child‘])
            response += tpl % (active, title, content)
        return response

    def actions(self):
        """
        检查当前用户是否对当前URL有权访问,并获取对当前URL有什么权限
        """
        action_list = []
        # 当前所有权限
        # {
        #     ‘/index.html‘: [‘GET‘,POST,]
        # }
        # 如果想把权限扩展到具体的几行数据,那么权限管理表中额外的添加参数
        for k, v in self.permission2action_dict.items():
            if re.match(k, self.current_url):
                action_list = v  # [‘GET‘,POST,]
                break
        return action_list

def permission(func):
    def inner(request, *args, **kwargs):
        user_info = request.session.get(‘user_info‘)
        if not user_info:
            return redirect(‘/login.html‘)
        obj = MenuHelper(request, user_info[‘username‘])
        action_list = obj.actions()
        if not action_list:
            return HttpResponse(‘无权限访问‘)
        kwargs[‘menu_string‘] = obj.menu_tree()
        kwargs[‘action_list‘] = action_list
        return func(request, *args, **kwargs)
    return inner

def login(request):
    if request.method == "GET":
        return render(request, ‘login.html‘)
    else:
        username = request.POST.get(‘username‘)
        pwd = request.POST.get(‘pwd‘)
        obj = models.User.objects.filter(username=username, password=pwd).first()
        if obj:
            # obj.id,  obj.username
            # 当前用户信息放置session中
            request.session[‘user_info‘] = {‘nid‘: obj.id, ‘username‘: obj.username}

            # 获取当前用户的所有权限
            # 获取在菜单中显示的权限
            # 获取所有菜单
            # 放置session中
            MenuHelper(request, obj.username)
            return redirect(‘/index.html‘)
        else:
            return redirect(‘/login.html‘)

def logout(request):
    request.session.clear()
    return redirect(‘/login.html‘)

@permission
def index(request, *args, **kwargs):
    action_list = kwargs.get(‘action_list‘)
    menu_string = kwargs.get(‘menu_string‘)
    if "GET" in action_list:
        result = models.User.objects.all()
    else:
        result = []
    return render(request, ‘index.html‘, {‘menu_string‘: menu_string, ‘action_list‘: action_list})

menuhelper类里封装了所有我们需要的信息,我们只要在装饰器中调用它,并把所需要的参数传出来即可。

原文地址:https://www.cnblogs.com/yangziyao/p/9410818.html

时间: 2024-07-29 17:54:28

Django之权限管理插件的相关文章

Django之权限管理

Django权限管理之初步完整版 项目背景:这是一个权限管理系统(给一些角色和他们的权限指URL和页面可以删除的按钮比如:增删改查) 使用到了中间件,和初始化权限,使用了admin的后台管理系统. 我们这个是基于角色的权限访问控制(Role-Based Access Control)做一个组件. 首先建立一个项目工程里面有另个应用:app01与rbac, 我们在rbac中model中建立一些数据类型代码如下: from django.db import models # Create your

django之权限管理公共组件

公共组件使用 公共组件的基本搭建 在上一篇已经是学习如何搭建一个公共组件,可以拷贝到任何项目里面,实现权限的管理工作,今天再次学习下公共组件的使用 新建一个项目,并把公共组件拷贝到新项目中取,并且在setting中注册组件 中间件的注册 公共组件的配置管理 在中间件中,通过导入项目的setting文件,从里面导入变量信息,所以我们在setting里面设置了如下变量信息: # ############################## RBAC权限相关配置开始 #################

Jenkins 权限管理

一.要对用户进行管理首先下载一个权限管理插件,系统管理--->插件管理--->可选插件 Role-based Authorization Strategy 系统管理 ---->全局安装配置中 如下图 允许用户注册(让用户自己创建然后给他分配权限  此处创建user1.user2两个用户) 二.安装好在    系统管理  >>> 创建测试项目:A-DB.A-Web1.B-Web1 三.然后分别登录user1.user2用户查看 四.为了方便管理创建视图 原文来自:http

Jenkins 用户权限管理

Jenkins 用户权限管理 插件管理 搜索下面插件 并下载插件:Role-based Authorization Strategy 功能:全局安全配置下 --> 访问控制下回多出Role-based选项 默认注册用户 (拥有管理员权限功能) 1.进入安全配置下--> 启用用户允许登录,并保存.配置如下图 2.进入登录页面,手动选择注册用户 3.填写注册信息,点击注册 4.登录成功.为了下面演示在创建user2角色. 使用权限管理用户配置 1.全局安全配置 --> 打开Role选项配置,

django 自定义user使用权限管理模块

这篇文章主要是讲如何让自定义的user模块也能用到django.contrib.auth中的权限管理模块 看这篇文章之前请先看一下我前边的两篇文章,本文以这两篇文章为基础: django 自定义 USER 用源码告诉你django权限管理是怎么回事 下边是一个大概的实现,后边再做详细分析: 1.user model自定义 class AbstractUser(models.Model): # 登录信息 id = models.AutoField(primary_key=True) staff =

2.4、使用Django自带的admin用户管理,权限管理

如何创建项目请参考2.2.创建项目. 通常web服务会要求注册的用户通过用户名和密码登录,然后才可能管理自己的信息或者对一些页面进行授权,判断用户是否拥有执行某种操作的权限. Django已经提供了一个django.contrib.auth应用来处理登录,登出和权限验证,同时还提供了django.contrib.admin应用来管理用户.可以参考你的虚拟python环境的/lib/python27/site-packges/django/contrib/admin里面的文件和源码. 我们要做的就

django 基于proxy实现用户权限管理

项目中经常会遇到用户权限管理的问题,django adminsite已经提供非常实用的用户权限管理机制.不过有些时候,我们希望根据相关用户属性来过滤adminsite中显示的内容.下文将结束如何实现: 原始类 假设我有这么一个问卷类,基于这个类可以实现增删改查的功能 class wenjuan(models.Model): """ 问卷 """ name=models.CharField(u'问卷名称',max_length=128) breif

[Django]用户权限学习系列之设计自有权限管理系统设计思路

若在阅读本片文章遇到权限操作问题,请查看本系列的前两章! http://www.cnblogs.com/CQ-LQJ/p/5609690.html和http://www.cnblogs.com/CQ-LQJ/p/5604331.html 现在步入正题,这篇文章是关于自有权限管理系统设计的思路描述,自有权限管理系统是抛弃django自带的后台管理界面,基于自己编写的权限管理界面对用户权限的管理! 首先上图:(自己设计的权限系统界面,代码将后续文章中写出) 权限管理界面主要是添加和删除权限,查看官方

Django—内置用户权限管理

内置用户权限管理 对于注册.登录.验证等功能我们可以自己编写用户管理应用,但Django也有一个内置的用户权限管理系统.也是很强大的. 在哪可以看到? 关于用户的信息都存放在这个表中. auth模块 from django.contrib import auth 其中有几个常用的方法: authenticate() 提供了用户认证功能,即验证用户名以及密码是否正确,一般需要username .password两个关键字参数. 如果认证成功(用户名和密码正确有效),便会返回一个 User 实例对象