参考: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): .....
- 拿到用户请求的url+请求方法+参数到我们的的perm_dic里去一一匹配
- 当匹配到了对应的权限条目后,就拿着这个条目所对应的权限名,和当前的用户, 调用request.user.has_perm(权限名)
- 如果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