基於角色的權限控制系統(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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM