初識rbac

1、權限組件

一、項目與應用

  一個項目能夠有多個應用;一個應用能夠在多個項目下;前提:應用是組件。html

二、什麼是權限?

  一個包含正則表達式的url就是一個權限。python

  能夠理解爲以下方程式:正則表達式

who    what   how   ---------->True  or  Flase

三、以路飛網站爲例,哪些人擁有哪些權限如何設計表

(1)初始設計

  1)用戶表UserInfor數據庫

id    用戶名    操做級別
1    admin       5
2    user        1

  2)權限表Permissiondjango

id    操做    操做級別
1    select     1
2    add        2
3    del        3

(2)根據url就是一個權限將表關係作以下修改

  1)用戶表bash

name   pwd    
egon   123            
alex   456    

  2)權限表session

id           url            title
1	"/users/"         "查看用戶"
2	"/users/add/"     "添加用戶"
3       "/customer/add"   "添加客戶"

   3)用戶與權限關係表(多對多)app

id    user_id   permission_id
1       1           1		
2       1           2
3       2           2

 (3)以登陸人egon爲例訪問url:http://127.0.0.1:8000/users/

def users(request):
    user_id = request.session.get("user_id")

    # 基於對象的跨表查詢
    obj = UserInfor.objects.filter(pk=user_id).first()
    # obj.permission.all()  拿到這個用戶全部的關聯權限
    obj.permission.all().valuelist("url")

    return HttpResponse("users...")

  能夠看到egon擁有查看用戶權限,所以能夠正常訪問http://127.0.0.1:8000/users/。函數

(4)這種實現方式存在的問題?

  這種方式是給人定權限,每每每一個人擁有多個權限,那每一個人都要在用戶與權限關係表擁有多條權限記錄,現實中每每不少人有相同的職責和權限,則權限記錄還要根據人數翻倍,數據庫存儲的數據量過大。網站

  所以應該給角色定權限,直接給人分配角色。

四、給角色定權限

(1)設計表和表關係

  1)userInfor用戶表

name   pwd    
egon   123            
alex   456    

  2)Role角色表

 id   title
 1    銷售員

  3)UserInfo2Role用戶角色關係表(多對多)

  id     user_id    role_id
  1        1          1

  4)Permission權限表

id          url            title
1	"/users/"         "查看用戶"
2	"/users/add/"     "添加用戶"
3       "/customer/add"   "添加客戶"

  5)Role2Permission角色權限關係表(多對多)

 id  role_id   permission_id
  1      1           1
  2      1           2
  3      1           3

  以上就設計了一個簡單的rbac數據表。

(2)什麼是rbac?

  rbac就是role-based access control,也就是基於角色的訪問控制

2、構建一個獨立的權限管理組件(隨時插拔使用)

一、建立一個django項目,創建應用app01和rbac

  

  因爲要建立的權限組件是一個獨立組件,新建一個rbac應用:

  (1)運行Tools——》Run manage.py Task...

  (2)在manage命令框執行:startapp rbac

manage.py@learn_rbac > startapp rbac
bash -cl "/Users/hqs/venv/bin/python /Applications/PyCharm.app/Contents/helpers/pycharm/django_manage.py startapp rbac /Users/hqs/PycharmProjects/learn_rbac"

  (3)在settings.py的INSTALLED_APPS中添加這個應用:

INSTALLED_APPS = [
    ...
    'django.contrib.staticfiles',
    'app01.apps.App01Config',
    'rbac.apps.RbacConfig',
]

   這個沒有添加會致使models裏的內容找不到等問題。

二、構建models.py

  /rbac/models.py:

class User(models.Model):
    name = models.CharField(max_length=32)
    pwd = models.CharField(max_length=32)
    roles = models.ManyToManyField(to="Role")

    def __str__(self):
        return self.name


class Role(models.Model):
    title = models.CharField(max_length=32)
    url = models.CharField(max_length=32)
    permissions = 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)

    def __str__(self):
        return self.title

   接着進行數據遷移:

$ python3 manage.py makemigrations

$ python3 manage.py migrate

三、運用Django的admin組件進行後臺數據管理

(1)建立超級用戶來登陸和使用admin組件

(venv)MacBook-Pro:learn_rbac hqs$ python3 manage.py createsuperuser
Username (leave blank to use 'hqs'): yuan
Email address: 
Password: 
Password (again): 
Superuser created successfully.

  建立好新的超級用戶的帳號密碼:yuan/yuan1234,訪問並登陸http://127.0.0.1:8000/admin

(2)admin註冊

  /rbac/admin.py:

from django.contrib import admin

# Register your models here.
from .models import *

admin.site.register(User)
admin.site.register(Role)
admin.site.register(Permission)

  註冊完成頁面顯示以下:

  

(3)admin頁面添加權限、角色、用戶

  

  

  

  建立了CEO、銷售、保潔三種角色:CEO具有全部權限、銷售具有查看和添加用戶權限、保潔只有查看用戶權限。

  建立三名用戶,yuan分配CEO角色、egon分配保潔、alex分配保潔和銷售角色。

(4)編輯/learn_rbac/learn_rbac/urls.py

from django.contrib import admin
from django.urls import path
from app01 import views

urlpatterns = [
    path('admin/', admin.site.urls),
    path('users/', views.users),
    path('users/add/', views.add_user),
    path('roles/', views.roles),
    path('login/', views.login),
]

(5)編輯/learn_rbac/app01/views.py

from django.shortcuts import render, HttpResponse

# Create your views here.
from rbac.models import *


def users(request):
    user_list = User.objects.all()
    return render(request, "users.html", locals())


def add_user(request):
    return HttpResponse("add user....")


def roles(request):
    role_list = Role.objects.all()
    return render(request, "roles.html", locals())

四、登陸驗證(session permission_list)

 learn_rbac/app01/views.py:

def login(request):
    if request.method == "POST":
        user = request.POST.get("user")
        pwd = request.POST.get("pwd")
        user = User.objects.filter(name=user, pwd=pwd).first()
        if user:
            # 保存登陸狀態,request.session

            ############### 在session中註冊用戶id ################
            request.session["user_id"] = user.pk

            ############### 在sessions中註冊權限列表 #############

            # 登陸成功
            # 查詢當前登陸用戶的全部角色
            ret = user.roles.all()
            print(ret)   # <QuerySet [<Role: 保潔>, <Role: 銷售>]>

            # 查看當前用戶全部的權限
            # 一、用values()來遍歷QuerySet;  二、跨表查詢  三、distinct去重
            permissions = user.roles.all().values("permissions__url").distinct()
            print(permissions)  # <QuerySet [{'permissions__url': '/users/'}, {'permissions__url': '/users/add'}]>

            permission_list = []
            for item in permissions:
                permission_list.append(item["permissions__url"])

            print(permission_list)  # ['/users/', '/users/add']

            # ret = user.roles.all().values("title", "permissions__url")
            # print(ret)  # <QuerySet [{'title': '保潔', 'permissions__url': '/users/'}, {'title': '銷售', 'permissions__url': '/users/'}, {'title': '銷售', 'permissions__url': '/users/add'}]>

            request.session["permission_list"] = permission_list

            return HttpResponse("登陸成功!")

    return render(request, "login.html")

   注意:

(1)用request.session保存登陸狀態,在session中註冊用戶ID:

request.session["user_id"] = user.pk

   在sessions中註冊權限列表:

request.session["permission_list"] = permission_list

(2)user.roles.all()查詢到當前登陸用戶的全部角色:<QuerySet [<Role: 保潔>, <Role: 銷售>]>

  user.roles.all().values("permissions__url")查詢到當前用戶全部權限,這裏須要注意三點:1.用values()來遍歷QuerySet;  2.跨表查詢  3.values不會去重,須要用distinct去重

(3)針對vlues的解析:

ret = user.roles.all().values("title", "permissions__url")
print(ret)  # <QuerySet [{'title': '保潔', 'permissions__url': '/users/'}, {'title': '銷售', 'permissions__url': '/users/'}, {'title': '銷售', 'permissions__url': '/users/add'}]>

"""上面的代碼能夠解釋爲以下步驟:
temp = []
for role in user.roles.all():
    temp.append({
    "title": role.title,
    "permissions_url": role.permissions.url,
     })
"""

五、權限校驗

from rbac.models import *
import re

def add_user(request):
    permission_list = request.session["permission_list"]  # ['/users/', '/users/add', '/users/delete/(\\d+)', '/users/edit/(\\d+)']

    current_path = request.path_info   # 當前路徑的屬性

    flag = False
    for permission in permission_list:
        permission = "^%s$" % permission
        ret = re.match(permission, current_path)  # 第一個參數是匹配規則,第二個參數是匹配項
        if ret:
            flag = True
            break
    if not flag:
        return HttpResponse("沒有訪問權限!")


def roles(request):
    permission_list = request.session["permission_list"]

    current_path = request.path_info  # 當前路徑的屬性

    flag = False
    for permission in permission_list:
        permission = "^%s$" % permission
        ret = re.match(permission, current_path)  # 第一個參數是匹配規則,第二個參數是匹配項
        if ret:
            flag = True
            break
    if not flag:
        return HttpResponse("沒有訪問權限!")

    role_list = Role.objects.all()
    return render(request, "roles.html", locals())

 注意:

(1)利用re.match讓匹配項去匹配匹配規則,確認用戶是否有對應的url即權限。

import re

ret = re.match('/users/', "/users/delete/9")
print(ret)  # 匹配成功:<_sre.SRE_Match object; span=(0, 7), match='/users/'>

# 這兩個字段一個是查詢、一個是刪除權限,應該是不匹配成功的
ret = re.match('^/users/$', "/users/delete/9")
print(ret)  # 匹配失敗:None

   第一個參數是匹配規則,第二個參數是匹配項,須要注意給匹配規則添加^和$來確保匹配規則正常生效。

(2)因爲刪除路徑通常是"/user/delete/數字"的形式,所以在添加編輯、刪除等權限是使用的url以下所示:

  

  

(3)驗證這條url是否在權限組中方式:

l = ['/users/', '/users/add', '/users/delete/(\d+)', '/users/edit/(\d+)']

c_path = "/users/delete/9"


flag = False
for permission in l:
    permission = "^%s$" % permission
    ret = re.match(permission, c_path)
    if ret:
        # 匹配成功有一個對象
        flag = True


if flag:
    print("success")

(4)能夠看到這樣須要在每一個視圖函數中添加一樣的代碼來實現權限控制,所以仍是須要基於中間件來實現權限校驗。

六、基於中間件的權限校驗

import re
from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponse, redirect


class ValidPermission(MiddlewareMixin):

    def process_request(self, request):

        # 當前訪問路徑
        current_path = request.path_info  # 當前路徑的屬性

        ########### 檢查是否屬於白名單 #############
        valid_url_list = ['/login/', '/reg/', '/admin/.*']
        for valid_url in valid_url_list:
            ret = re.match(valid_url, current_path)
            if ret:
                return   # 等同於return none

        ############### 檢驗是否登陸 ##############
        user_id = request.session.get("user_id")

        if not user_id:
            return redirect("/login/")

        ################ 校驗權限 #################
        permission_list = request.session.get("permission_list")

        flag = False
        for permission in permission_list:
            permission = "^%s$" % permission
            ret = re.match(permission, current_path)  # 第一個參數是匹配規則,第二個參數是匹配項
            if ret:
                flag = True
                break
        if not flag:
            # 若是沒有訪問權限
            return HttpResponse("沒有訪問權限!")

注意:

(1)在這裏建立了/rbac/service子目錄,並在目錄下建立rbac.py來自定義一箇中間件。

(2)在settings中添加自定義中間件:

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    ...
    'rbac.service.rbac.ValidPermission',
]

 (3)白名單,不須要任何權限局能訪問的url

valid_url_list = ['/login/', '/reg/', '/admin/.*']
for valid_url in valid_url_list:
    ret = re.match(valid_url, current_path)
    if ret:
        return   # 等同於return none

八、分拆login視圖函數,將查看當前用戶全部的權限取出做爲一個模塊引用使用

views.py:

from rbac.service.permissions import *

def login(request):
    if request.method == "POST":
        user = request.POST.get("user")
        pwd = request.POST.get("pwd")
        user = User.objects.filter(name=user, pwd=pwd).first()
        if user:
            # 保存登陸狀態,request.session
            ############### 在session中註冊用戶id ################
            request.session["user_id"] = user.pk

            ############### 在sessions中註冊權限列表 #############
            initial_session(user, request)

            return HttpResponse("登陸成功!")
    return render(request, "login.html")

 /rbac/service/permissions.py:

def initial_session(user,request):
    """
    查看當前用戶全部的權限
    :param user:
    :param request:
    :return:
    """
    permissions = user.roles.all().values("permissions__url").distinct()
    print(permissions)  # <QuerySet [{'permissions__url': '/users/'}, {'permissions__url': '/users/add'}]>

    permission_list = []
    for item in permissions:
        permission_list.append(item["permissions__url"])

    print(permission_list)

    request.session["permission_list"] = permission_list
相關文章
相關標籤/搜索