前言:

一.用户角色权限设计思路:

<1>不同职责的人员,对于系统操作的权限应该是不同;
<2>可以对“组”进行权限分配;
<3>权限管理系统应该是可扩展的;
<4>满足业务系统中的功能权限

2. 角色和操作的权限设计(
不同的应用场合,你可能会想出不同的需求,提了一个新的需求以后,可能会发现原来的设计没方法实现了,于是还要添加一个新的表。这也是上面所提到的问题。 其实不必想得那么复杂,权限可以简单描述为:

某某主体 在 某某领域 有 某某权限

1,主体可以是用户,可以是角色,也可以是一个部门

2, 领域可以是一个模块,可以是一个页面,也可以是页面上的按钮

3, 权限可以是“可见”,可以是“只读”,也可以是“可用”(如按钮可以点击)

其实就是Who、What、How的问题, 谁对什么功能,动作有怎样的操作权限

下面来看看表结构是如何设计的,代码如下:

  1. from django.db import models
  2.  
  3. # Create your models here.
  4.  
  5. class Permission(models.Model):
  6. caption = models.CharField(max_length=32)
  7. parent_id = models.ForeignKey('Permission', related_name='k', to_field='id', null=True, blank=True)
  8. code = models.CharField(max_length=64, null=True,blank=True)
  9. method = models.CharField(max_length=16, null=True,blank=True)
  10. kwargs = models.CharField(max_length=128, null=True,blank=True)
  11. is_menu = models.BooleanField(default=False)
  12.  
  13. def __str__(self):
  14. return self.caption
  15.  
  16. class Role(models.Model):
  17. name = models.CharField(max_length=32)
  18. def __str__(self):
  19. return self.name
  20.  
  21. class RoleToPermission(models.Model):
  22. menu_id = models.ForeignKey(Permission, to_field='id')
  23. role_id = models.ForeignKey(Role, to_field='id')
  24.  
  25. def __str__(self):
  26. return "%s-%s" %(self.menu_id.caption, self.role_id.name)
  27. # 目标,根据角色列表获取权限 li
  28. # 获取当前用户的所有标题权限
  29. # RoleToPermission.objects.filter(role_id__in=li,menu_id__is_menu=True).\
  30. # values('menu_id__caption','menu_id__parent_id','menu_id__parent_id','menu_id__code')
  31.  
  32. # 获取当前用户的所有权限
  33. # RoleToPermission.objects.filter(role_id__in=li).\
  34. # values('menu_id__caption','menu_id__parent_id','menu_id__parent_id','menu_id__code')
  35.  
  36. class UserInfo(models.Model):
  37. username = models.CharField(max_length=32)
  38. password = models.CharField(max_length=64)
  39.  
  40. def __str__(self):
  41. return self.username
  42.  
  43. class UserInfoToRole(models.Model):
  44. user_id = models.ForeignKey(UserInfo, to_field='id')
  45. role_id = models.ForeignKey(Role, to_field='id')
  46. def __str__(self):
  47. return '%s-%s' %(self.user_id.username, self.role_id.name)

用户,角色, 权限表

权限表直接将权限分为具体的标题级别,url或者具体的某种操作方法

##权限表与角色表关系

先来看看权限表的表结构是如何构造的:

  1. class Permission(models.Model):
  2. ##菜单的的权限,主要是为后台界面菜单部分单独拆分出来的字段,跟下面定义的code,method,kwargs等字段进行区分
  3. caption = models.CharField(max_length=32)
  4. ##用于构造树形结构的权限表,可以自己关联自己
  5. parent_id = models.ForeignKey('Permission', related_name='k', to_field='id', null=True, blank=True)
  6. ##具体的某个访问的url
  7. code = models.CharField(max_length=64, null=True,blank=True)
  8. ##具体的请求方法
  9. method = models.CharField(max_length=16, null=True,blank=True)
  10. ##请求方法所带的参数
  11. kwargs = models.CharField(max_length=128, null=True,blank=True)
  12. ##是否是菜单栏
  13. is_menu = models.BooleanField(default=False)
  14.  
  15. def __str__(self):
  16. return self.caption

从上边的表可以看出,我们将权限分类处理,这样可以更清晰准确的针对不同类型的权限进行划分,并且构造出了有关联关系的树形结构

接下来将权限表与角色表通过多对多关联,代码如下:

  1. class Role(models.Model):
  2. #定义角色名称
  3. name = models.CharField(max_length=32)
  4. def __str__(self):
  5. return self.name
  6.  
  7. class RoleToPermission(models.Model):
  8. ##定义角色和权限的对应关系
  9. menu_id = models.ForeignKey(Permission, to_field='id')
  10. role_id = models.ForeignKey(Role, to_field='id')
  11.  
  12. def __str__(self):
  13. return "%s-%s" %(self.menu_id.caption, self.role_id.name)
  14. # 目标,根据角色列表获取权限 li
  15. # 获取当前用户的所有标题权限
  16. # RoleToPermission.objects.filter(role_id__in=li,menu_id__is_menu=True).\
  17. # values('menu_id__caption','menu_id__parent_id','menu_id__parent_id','menu_id__code')

##用户与角色关系

用户表UserInfo 通过中间表UserInfoToRole 多对多关联 角色表Role,具体代码如下

  1. class Role(models.Model):
  2. name = models.CharField(max_length=32)
  3. def __str__(self):
  4. return self.name
  5.  
  6. class UserInfo(models.Model):
  7. username = models.CharField(max_length=32)
  8. password = models.CharField(max_length=64)
  9.  
  10. def __str__(self):
  11. return self.username
  12.  
  13. class UserInfoToRole(models.Model):
  14. user_id = models.ForeignKey(UserInfo, to_field='id')
  15. role_id = models.ForeignKey(Role, to_field='id')
  16. def __str__(self):
  17. return '%s-%s' %(self.user_id.username, self.role_id.name)

##登录过程,根据用户信息,关联查询获取用户相关角色,根据用户所属的角色关联查询用户对应的权限,具体实现代码如下:

  1. def login(request):
  2. if request.method == 'POST':
  3. user = request.POST.get('user')
  4. pwd = request.POST.get('pwd')
  5. obj = models.UserInfo.objects.filter(username=user, password=pwd).first()
  6. if obj:
  7. # 当前用户信息保存至Session
  8. request.session['user_info'] = {'id': obj.id, 'name': obj.username}
  9.  
  10. # 当前用户角色列表保存至Session
  11. result_list = models.UserInfoToRole.objects.filter(user_id_id=obj.id).values('role_id_id')
  12. role_list = list(map(lambda x: x['role_id_id'], result_list))
  13. request.session['role_list'] = role_list
  14.  
  15. # 当前用户所有权限加入Session
  16. from django.db.models import Count, Min, Max, Sum
  17. permission_list = models.RoleToPermission.objects.filter(role_id__in=role_list).values(
  18. 'menu_id_id').annotate(c=Count('menu_id_id')).values('menu_id__caption',
  19. 'menu_id__parent_id',
  20. 'menu_id__code',
  21. 'menu_id__method',
  22. 'menu_id__kwargs',
  23. 'menu_id__id')
  24. # 根据permission_id去重
  25. permission_list = list(permission_list)
  26. request.session['permission_list'] = permission_list
  27.  
  28. menu_list = models.RoleToPermission.objects.filter(role_id__in=role_list,menu_id__is_menu=True).values(
  29. 'menu_id_id').annotate(c=Count('menu_id_id')).values('menu_id__caption',
  30. 'menu_id__parent_id',
  31. 'menu_id__code',
  32. 'menu_id__method',
  33. 'menu_id__kwargs',
  34. 'menu_id__id',)
  35. # 根据permission_id去重
  36. menu_list = list(menu_list)
  37. request.session['menu_list'] = menu_list
  38.  
  39. return redirect('/index/')
  40. return render(request, 'login.html')

Login

#自定义render,从session中获取menu_list,代码如下:

  1. def my_render(request, template_name, context=None, *args, **kwargs):
  2. session_menu_list = request.session['menu_list']
  3. menu_list = build_tree(session_menu_list)
  4. print(request.path_info.split('/')[1])
  5. if context:
  6. context['menu_list'] = menu_list
  7. else:
  8. context = {'menu_list': menu_list}
  9. return render(request, template_name, context, *args, **kwargs)

my_render

#定义装饰器validator,用来验证用户所请求的url是否已经过授权,代码如下:

  1. def validate(func):
  2. def inner(request,*args,**kwargs):
  3. session_perm_l = list(map(lambda x : x['menu_id__code'] , request.session['permission_list']))
  4. if request.path_info.split('/')[1] in session_perm_l:
  5. ret = func(request,*args,**kwargs)
  6. return ret
  7. else:
  8. return HttpResponse('unauhoted')
  9. return inner

Validate

#通过递归方法构建权限树形结构,代码如下:

  1. def build_node(menu_list, dic):
  2. #
  3. for menu in menu_list:
  4. if menu['id'] == dic['menu_id__parent_id']:
  5. temp = {'id': dic['menu_id__id'],'text': dic['menu_id__caption'], 'url': dic['menu_id__code'],'children': []}
  6. menu['children'].append(temp)
  7. break
  8. else:
  9. build_node(menu['children'], dic)
  10.  
  11. def build_tree(session_menu_list):
  12. # [ {menu_id__parent_id: None, 'menu_id__caption': '权限管理', 'menu_id__code': 'permission'},{},{} ]
  13. menu_list = []
  14. # menu_list = [{...}]
  15. for dic in session_menu_list:
  16. if dic['menu_id__parent_id'] == None:
  17. temp = {'id': dic['menu_id__id'],'text': dic['menu_id__caption'], 'url': dic['menu_id__code'],'children': []}
  18. menu_list.append(temp)
  19. else:
  20. # 当前
  21. build_node(menu_list, dic)
  22. return menu_list

build_tree

下面看下前端是如何实现的,前端使用EasyUI, 界面比较老旧,不过前端已经高度封装,有利于提高开发效率,首先我们先构建自己的母版,将基本的标题和样式,js文件,并展示出左侧菜单按钮

具体实现方法如下:

首先EasyUI以west,east,north,south,center 将前端分文左右上下中间的布局效果,具体实现代码如下:

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>Full Layout - jQuery EasyUI Demo</title>
  6. <link rel="stylesheet" type="text/css" href="/static/easyui/themes/default/easyui.css">
  7. <link rel="stylesheet" type="text/css" href="/static/easyui/themes/icon.css">
  8. <script type="text/javascript" src="/static/easyui/jquery.min.js"></script>
  9. <script type="text/javascript" src="/static/easyui/jquery.easyui.min.js"></script>
  10. <style>
  11. .crm-menu{
  12. display: block;
  13. padding: 8px;
  14. border-bottom: 1px dotted #dddddd;
  15. }
  16. .crm-menu:hover{
  17. background-color: #E0ECFF;
  18. }
  19. </style>
  20. {% block css %} {% endblock %}
  21. </head>
  22. <body class="easyui-layout">
  23. <div data-options="region:'north',border:false" style="height:60px;background:#B3DFDA;padding:10px">
  24. LOGO
  25. </div>
  26. <div data-options="region:'west',split:true,title:'West'" style="width:150px;">
  27. <div id="aa" class="easyui-accordion" style="width:100%;">
  28.  
  29. {% for menu in menu_list %}
  30. <div title="{{ menu.text }}" data-options="iconCls:'icon-ok'" style="overflow:auto;">
  31.  
  32. {% for child in menu.children %}
  33. <a id="menu_{{ child.url }}" href='/{{ child.url }}/' class='crm-menu'>{{ child.text }}</a>
  34. {% endfor %}
  35. </div>
  36. {% endfor %}
  37.  
  38. </div>
  39.  
  40. </div>
  41. <div data-options="region:'south',border:false" style="height:50px;background:#A9FACD;padding:10px;">south region</div>
  42. <div data-options="region:'center',title:'Center'">
  43. {% block content %} {% endblock %}
  44. </div>
  45.  
  46. {% block js %} {% endblock %}
  47. </body>
  48. </html>

_layout.html

母版定义好后,我们可以基于母版定义具有树形结构的权限展示页面.

EasyUI可以通过两种方式实现此效果,通过“easyui_tree” 类实现树形菜单,也可以通过js生成前端展示页面,具体代码如下:

  1. {% extends 'layout/_layout.html' %}
  2.  
  3. {% block content %}
  4.  
  5. <div style="float: left;width: 300px">
  6. <ul id="pers_tree" ></ul>
  7. <!-- <ul id="tt" class="easyui-tree" data-options="url:'/get_permission_tree/',method:'get',animate:true"></ul> -->
  8. </div>
  9.  
  10. <div style="float: left;width: 600px">
  11. <table id="dg"></table>
  12. </div>
  13. <div id="dlg" class="easyui-dialog" style="width:400px;height:200px;padding:10px 20px" closed="true" buttons="#dlg-buttons"> <!-#easyui订制模态对话框,默认关闭状态->
  14. <form id="fm1">
  15. <div class="input-group clearfix">
  16. <div class="group-label" style="width: 80px;">
  17. <span>省份:</span>
  18. </div>
  19. <div class="group-input" style="width: 300px;">
  20. <input id="dlg_nid" style="width: 200px;display: none" name="nid"/>
  21. <input id="dlg_province" style="width: 200px" class="easyui-textbox" type="text" name="caption" data-options="required:true,missingMessage:'省份不能为空'" />
  22. </div>
  23. </div>
  24. </form>
  25. </div>
  26. <div id="dlg-buttons"> 
  27. <span id="dlg_summary" style="color: red"></span>
  28. <a href="#" class="easyui-linkbutton" iconCls="icon-ok" onclick="Save()">保存</a>
  29. <a href="#" class="easyui-linkbutton" iconCls="icon-cancel" onclick="javascript:$('#dlg').dialog('close')">取消</a>
  30. </div>
  31. {% endblock %}
  32.  
  33. {% block js %}
  34. <script>
  35. $(function(){
  36. InitTree();
  37. });
  38.  
  39. function InitTree(){
  40. $('#pers_tree').tree({
  41. url: '/get_permission_tree/',
  42. method: 'get',
  43. animate: true,
  44. onClick: function(node){
  45. console.log(node.text,node.id);
  46. InitTable(node.id);
  47. InitPagination();
  48. }
  49. })
  50. }
  51.  
  52. function InitTable(node_parent_id){
  53. $('#dg').datagrid({
  54. title: '听不下去了',
  55. url: '/get_child_permission/',
  56. method: 'get',
  57. queryParams: {
  58. node_parent_id: node_parent_id
  59. },
  60. columns: [[
  61. {
  62. field: 'ck',
  63. checkbox: true
  64. },
  65. {
  66. field: 'caption',
  67. title: '标题',
  68. width: 180,
  69. align: 'center'
  70. },
  71. {
  72. field: 'code',
  73. title: 'URL',
  74. width: 180,
  75. align: 'center'
  76. }
  77.  
  78. ]],
  79. toolbar: [
  80. {
  81. text: '添加',
  82. iconCls: 'icon-add',
  83. handler: AddRow
  84. }, {
  85. text: '删除',
  86. iconCls: 'icon-remove',
  87. handler: RemoveRow
  88. }, {
  89. text: '修改',
  90. iconCls: 'icon-edit',
  91. handler: EditRow
  92. }
  93. ],
  94. pagePosition: 'both',
  95. pagination: true,
  96. pageSize: 10,
  97. pageNumber: 1,
  98. pageList: [10, 20, 50]
  99. })
  100. }
  101.  
  102. function AddRow(){
  103. // 显示对话框,由于希望添加则将方法设置为POST
  104. $('#fm1').form('clear');
  105. $('#dlg').dialog('open').dialog('setTitle','创建省份');
  106. $('#dlg_summary').empty();
  107. METHOD = 'post';  
  108. }
  109. function RemoveRow(){
  110. console.log('RemoveRow');
  111. }
  112. function EditRow(){
  113. console.log('EditRow');
  114. }
  115.  
  116. function InitPagination() {
  117. var pager = $('#dg').datagrid('getPager');
  118. $(pager).pagination({
  119. beforePageText: '第',
  120. afterPageText: '页 共{pages}页',
  121. displayMsg: '当前显示{from}-{to}条记录 共{total}条数据'
  122. })
  123. }
  124. </script>
  125. {% endblock %}

EasyUI 已经高度封装了树形菜单的实现方法,我们只要了解具体的参数即可,详见http://www.jeasyui.com/demo/main/index.php

基于EasyUI 快速搭建权限管理平台的更多相关文章

  1. 基于EasyUI Treegrid的权限管理资源列表

    1. 前言 最近在开发系统权限管理相关的功能,主要包含用户管理,资源管理,角色管理,组类别管理等小的模块.之前的Web开发中也用过jQueryEasyUI插件,感觉这款插件简单易用,上手很快.以前用到 ...

  2. EASYUI+MVC4通用权限管理平台

    通用权限案例平台在经过几年的实际项目使用,并取得了不错的用户好评.在平台开发完成后,特抽空总结一下平台知识,请各位在以后的时间里,关注博客的更新. 1.EASYUI+MVC4通用权限管理平台--前言 ...

  3. (转)EASYUI+MVC4通用权限管理平台

    原文地址:http://www.cnblogs.com/hn731/archive/2013/07/15/3190947.html 通用权限案例平台在经过几年的实际项目使用,并取得了不错的用户好评.在 ...

  4. EASYUI+MVC4通用权限管理平台--前言

    经过多年的管理信息系统的开发工作,吸取了工作中遇到的一些问题,经过自己的总结,形成了一套比较完整的管理信息系统的通用权限管理基础开发平台. 在软件的开发过程中我们首先需要解决的是UI问题,其次是浏览器 ...

  5. vue.js快速搭建图书管理平台

      前  言 上一期简单讲解了vue的基本语法,这一次我们做一个小项目,搭建一个简单的图书管理平台,能够让我们更深刻的理解这门语言的妙用.   1.DEMO样式 首先我们需要搭建一个简单的demo样式 ...

  6. 基于fastadmin快速搭建后台管理

    FastAdmin是一款基于ThinkPHP5+Bootstrap的极速后台开发框架:开发文档 下面对环境搭建简要概述,希望后来者能少走弯路: 1. 百度搜索最新版wampserver, 安装并启动 ...

  7. 基于 Jenkins 快速搭建持续集成环境--转

    源地址:http://www.ibm.com/developerworks/cn/java/j-lo-jenkins/ 持续集成是一种软件开发实践,对于提高软件开发效率并保障软件开发质量提供了理论基础 ...

  8. Docker: 快速搭建LNMP网站平台

    快速搭建LNMP网站平台 步骤: 1.自定义网络(这里建立一个自定义网络,名字叫 lnmp, 让LNMP网站的服务,都加入这个自定义网络)docker network create lnmp2.创建M ...

  9. Jenkins配置基于角色的项目权限管理--转

    本文将介绍如何配置jenkins,使其可以支持基于角色的项目权限管理. 由于jenkins默认的权限管理体系不支持用户组或角色的配置,因此需要安装第三发插件来支持角色的配置,本文将使用Role Str ...

随机推荐

  1. mysql更改utf8编码方式

    方法1: 一.查看数据库编码格式 1 mysql> show variables like 'character_set_database'; 二.查看数据表的编码格式 1 mysql> ...

  2. sizeof 数组与指针

    在学习指针的时候,得到指针的定义和数组的定义一样,但是这时候就很好奇,指针只是一个地址,那数组和指针一样的话,sizeof时怎么得知其长度呢. 于是百度了下面的回复: 千万不要把数组名看成指针,尽管有 ...

  3. JQuery------制作div模态框

    转载: http://blog.csdn.net/li_xiao_ming/article/details/6738922 如图: 代码: html(使用opacity的话content无法变为不透明 ...

  4. ios开发之--CGRect/CGSize/CGPoint/CGVector/CGAffineTransform/UIEdgeInsets/UIOffset和NSString之间的转换

    仅做记录,一个函数和字符串之间的互相转换 方法如下: UIKIT_EXTERN NSString *NSStringFromCGPoint(CGPoint point); UIKIT_EXTERN N ...

  5. SQL Server计算列

    计算列由可以使用同一表中的其他列的表达式计算得来.表达式可以是非计算列的列名.常量.函数,也可以是用一个或多个运算符连接的上述元素的任意组合.表达式不能为子查询. 例如,在 AdventureWork ...

  6. Jquery判断某个标签 Id是否存在

    query判断某个标签 Id是否存在, 如果是下面的 jQuery 代码判断一个对象是否存在,是不能用的 if($("#id")){}else{} 因为 $(“#id”) 不管对象 ...

  7. PMP十大知识领域整理

    2018-7-28至2018-12-8历时4个多月,学写了PMP(拍马屁),感觉自己经历了,哇-唉-哦-嗯这四个阶段 刚开始觉得如遇圣经,被PMP的知识体系和老师的精彩课程深深震撼! 后来觉得很多东西 ...

  8. Spring MVC静态资源访问

    最近在学习servlet的时候发现自己不能访问到css和js, 于是google一番学到不少方法加载,总结如下: 1.对于Spring MVC, 由于我们截获了所有请求<url-pattern& ...

  9. 有关弱类型意识、DOM、动态语言与函数式编程

    一.弱类型意识  js变量是没有类型的 var a =1;   //a 就是一个变量  不要提类型 变量可以赋予任何类型的值,类型仅仅是值得性质  与变量无关   js 的基本类型 变量未赋值时,其值 ...

  10. Spring的泛型依赖注入

    Spring 4.x 中可以为子类注入子类对应的泛型类型的成员变量的引用,(这样子类和子类对应的泛型类自动建立关系)具体说明: 泛型注入:就是Bean1和Bean2注入了泛型,并且Bean1和Bean ...