zoukankan      html  css  js  c++  java
  • 基于角色的权限控制系统(role-based access control)

      role-based access control(rbac),指对于不同角色的用户,拥有不同的权限 。用户对应一个角色,一个角色拥有若干权限,形成用户-角色-权限的关系,如下图所示。当一个用户进行访问数据时,根据其角色判断其拥有的权限,限定其操作。通过django实现一个简单的rbac app,简要记录下过程。

    1.实现效果

      实现效果如下图所示,不同用户拥有不同角色,不同角色拥有不同权限。下图中只是对角色和用户两张表进行编辑和访问,当我们项目中还有其他数据需要进行权限控制访问时,只需进行两处设置,一是在权限表中为这个数据表增加 "增删改查" 四个权限,然后将对应权限添加到角色表中即可。

    2. 实现过程

      2.1 数据库设计

        建立四张表,用户表,角色表,权限表和权限组表。models.py代码如下:

    from __future__ import unicode_literals
    from django.db import models
    
    class User(models.Model):
        name = models.CharField(max_length=32)
        password = models.CharField(max_length=16)
        age = models.IntegerField()
        role = models.ForeignKey(to='Role')
        def __str__(self):
            return self.name
    
    class Role(models.Model):
        title = models.CharField(max_length=32)
        permission = models.ManyToManyField(to='Permission')
        def __str__(self):
            return self.title
    
    class Permission(models.Model):
        title = models.CharField(max_length=32)
        url = models.CharField(max_length=32)
        action = models.CharField(max_length=32)
        group = models.ForeignKey(to='PermissionGroup')
        def __str__(self):
            return self.title
    
    class PermissionGroup(models.Model):
        title = models.CharField(max_length=32)
        def __str__(self):
            return self.title
    models.py

        这里一条权限实际上对应一条url,表示对一张数据表的一个操作(增删改查),而权限分组则表明权限属于对那张表的操作。因此有几张可以操作的数据表应对应几个权限组,而每个权限组,应该都包括四个权限:增删改查。一条权限的数据信息如下图。这里只有用户表和角色表两张表可以操作,对应用户管理和角色管理两个权限组。

      2.2 权限控制

        用户是通过url来访问数据,而一条url对应一个权限。因此当用户访问数据时,权限控制的流程是:
          a, 首先判断用户是否登录,未登录时重定向至登陆页面。

          b,用户登陆进来时,根据其角色判断拥有的所有权限,并将其权限表计入session中。

          c,用户登陆后,当其访问数据时,根据session中权限表判断,若无查看权限则直接拒绝,否则允许查看,进一步判断增删改权限来个性化前端显示页面。

        url代码如下:

    # 总的一级路由 
    from
    django.conf.urls import url,include from django.contrib import admin urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'',include('rbac.urls')) ] # rbac.urls代码,二级分发路由 urlpatterns = [
       url(r'^login/$', views.login), url(r
    '^user/$', views.listUser), url(r'^user/add/', views.addUser), url(r'^user/edit/(d+)', views.editUser), url(r'^user/delete/(d+)', views.deleteUser), url(r'^role/$', views.listRole), url(r'^role/add/', views.addRole), url(r'^role/edit/(d+)', views.editRole), url(r'^role/delete/(d+)', views.deleteRole), ]

      url权限的判断,通过自定义中间件来实现,将用户访问的url和其权限表url匹配,拥有权限时才允许通过,交给相应的视图函数处理。代码如下:

    from django.utils.deprecation import MiddlewareMixin
    from django.shortcuts import HttpResponse,redirect
    import re
    #自定义中间件
    class ValidPermission(MiddlewareMixin):
        def process_request(self,request):
            current_path = request.path  # 拿到当前请求路径  '/user/'
           
         #设置白名单,允许任何人访问的url valid_urls = ['/login/','/admin/(.*)'] for url in valid_urls: path = '^%s$'%url ret = re.match(path, current_path) if ret: return None #对于没有登陆的用户重定向至登陆页面 user_id = request.session.get('user',[]) if not user_id: return redirect('/login/') #根据权限来匹配url,决定当前用户是否有访问权限 permission_list = request.session['permission_list'] # print permission_list,current_path for permission in permission_list.values(): urls = permission['permission__url'] for url in urls: path = '^%s$'%url ret = re.match(path,current_path) if ret: request.actions = permission['permission__action'] #[u'list', u'edit', u'add'] # print request.actions return None return HttpResponse('没有访问权限')

      登陆函数代码:

    def login(request):
        if request.method=='POST':
            name = request.POST.get('name')
            passsword = request.POST.get('password')
            # print name, passsword
            user_obj = models.User.objects.filter(name=name,password=passsword).first()
            if user_obj:
                request.session['user']=user_obj.pk
                initial_permission(request,user_obj)
                return redirect('/user/')
            else:
                return render(request,'login.html')
        return render(request, 'login.html')
    
    from rbac import models
    def initial_permission(request,user):
        permissions = models.Role.objects.filter(user=user).values('permission__url','permission__action','permission__group_id').distinct()
        permission_list = {}
        for item in permissions:
            if item['permission__group_id'] not in permission_list:
                permission_list[item['permission__group_id']]= {'permission__url':[item['permission__url'],],'permission__action':[item['permission__action'],]}
            else:
                permission_list[item['permission__group_id']]['permission__url'].append(item['permission__url'])
                permission_list[item['permission__group_id']]['permission__action'].append(item['permission__action'])
        #print permission_list
        request.session['permission_list'] = permission_list
        # 按用户组权限id分组,得到如下的数据结构,即对两张表分别的操作权限
        # {1: {'permission__url': [u'/user/', u'/user/edit/(\d+)', u'/user/add/'],
        #      'permission__action': [u'list', u'edit', u'add']},
        #  2: {'permission__url': [u'/role/'], 'permission__action': [u'list']}}
    
        #设置菜单的显示权限数据
        menu_permissions = models.Role.objects.filter(user=user).values('permission__url','permission__action','permission__group__title').distinct()
        menu_permission_list=[]
        for item in menu_permissions:
            if item['permission__action'] == 'list':         menu_permission_list.append((item['permission__url'],item['permission__group__title']))
        request.session['menu_permission_list'] = menu_permission_list
        # print menu_permission_list

      视图函数的处理主要时将数据和用户权限传给前端,前端根据权限来显示不同的页面(若用户拥有添加,删除,编辑权限,则显示增加,删除,编辑按钮,否则不显示),下面是查看用户表的视图函数和前端页面:

    class PermissionAction(object):
        def __init__(self,actions):
            self.actions = actions
        def add_check(self):
            return 'add' in self.actions
        def edit_check(self):
            return 'edit' in self.actions
        def delete_check(self):
            return 'delete' in self.actions
    # 查看用户表的视图函数
    def listUser(request):
        users = models.User.objects.all()
        user_id = request.session.get('user')
        user = models.User.objects.filter(id=user_id).first()
        permission_action = PermissionAction(request.actions)
        return render(request, 'rbac/listUser.html', locals())

    listUser.html

    {% extends 'rbac/base.html' %}
    {% block data_table %}
        <div>
            {% if permission_action.add_check %}
                <a href="/user/add/">
                    <button class="btn btn-success" style="margin-bottom: 15px">添加用户</button>
                </a>
            {% endif %}
        </div>
        <table class="table table-bordered ">
            <thead>
                <tr>
                    <th>用户名</th>
                    <th>角色</th>
                    <th>操作</th>
                </tr>
            </thead>
            <tbody>
                {% for user in users %}
                    <tr>
                    <td>{{ user.name }}</td>
                    <td>{{ user.role }}</td>
                    <td>
                        {% if permission_action.edit_check %}
                            <a href="/user/edit/{{ user.pk }}">
                                <button class="btn btn-success">编辑</button>
                            </a>
                        {% endif %}
                        {% if permission_action.delete_check %}
                            <a href="/user/delete/{{ user.pk }}">
                                <button class="btn btn-danger" style="margin-left:15px">删除</button>
                            </a>
                        {% endif %}
                    </td>
                    </tr>
                {% endfor %}
            </tbody>
            </table>
    {% endblock %}

    完整代码见github:https://github.com/silence-cho/Rbac

  • 相关阅读:
    Ajax入门
    多线程入门(五)
    多线程入门(四)
    多线程入门(三)
    多线程入门(二)
    多线程入门(一)
    git使用简介
    Windows远程登录到VirtualBox安装的Ubuntu11.10
    阿里面试2(转)
    百度java开发面试题
  • 原文地址:https://www.cnblogs.com/silence-cho/p/9781237.html
Copyright © 2011-2022 走看看