基于Django的RBAC组件(角色的访问控制)基础版(一)

关于RBAC

RBAC是基于角色的访问控制(Role-Based Access Control )在 RBAC 中,权限与角色相关联,用户通过成为适当角色的成员而得到这些角色的权限。这就极大地简化了权限的管理。这样管理都是层级相互依赖的,权限赋予给角色,而把角色又赋予用户,这样的权限设计很清楚,管理起来很方便。

RBAC实际上也就是 Who 、What、How之间的关系,也就是Who对What进行How的操作。
Who:是权限的拥有者或者主体
What:是具体的操作或对象(operation,object)
How:具体的权限(privilege,正向授权或负向授权)

关于权限
  1. 权限,如果一个网站没有权限管理的话,那么这个网站只要别人带着url即可访问的话,那就乱套了。
  2. 所以就有了权限管理。权限不一定就是rbac,但是rbac有其的优点
  3. 普通的权限
    user表
    id name pwd
    1 张三 123
    2 李四 456
    3 王五 789

permission表
id title url
1 查看用户 /user/
2 添加用户 /user/add/
3 删除用户 /delete/user/(\d+)/

user_permission表
id user_id permission_id
1 1 1
2 1 2
3 1 3
4 2 1
5 2 2
6 2 3
7 3 1
8 3 2
9 3 3
按照普通的设计的话,光是这几个url的权限就够繁琐的了,下面来看看RBAC的好处
user表
id name pwd
1 张三 123
2 李四 456
3 王五 789

permission表
id title url
1 查看用户 /user/
2 添加用户 /user/add/
3 删除用户 /delete/user/(\d+)/

role表
id title
1 CEO
2 前台
3 IT

role_permission表
id role_id permission_id
1 1 1
2 1 2
3 1 3
1 2 1
2 2 2
1 3 1
2 3 2
3 3 3

user_role
id user_id role_id
1 1 1
2 2 2
3 3 3

不知道你们有没看懂这个的好处。

RBAC开端
  1. 新建一个Django项目(rbac_test):
  2. 创建一个名字为app01 的app,用于当做普通的项目
  3. 再创建一个名字为rbac的app,这个用于存放权限相关的东东,这就是rbac组件了。
  4. 数据库的话我用django 自带的sql3了

目录结构如图:
在这里插入图片描述

rbac的models.py的代码:

from django.db import models


# Create your models here.


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 Permission(models.Model):
    title = models.CharField(max_length=32)
    url = models.CharField(max_length=128)
    roles = models.ManyToManyField(to='Role')

    def __str__(self):
        return self.title


class Role(models.Model):
    title = models.CharField(max_length=32)

    def __str__(self):
        return self.title

上面rbac的表格设计好了,使用makemigrations 和migrate生成表格,然后我们需要在我们项目里面添加一些url,也就需要添加一些视图。
在app01的views里面加入:

from django.shortcuts import render, HttpResponse, redirect

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


def login(request):
    if request.method == "POST":
        user = request.POST.get('name')
        pwd = request.POST.get('pwd')
        user_obj = User.objects.filter(name=user, pwd=pwd).first()
        if user_obj:
            return redirect('/user/')
        return render(request, 'login.html')

    return render(request, 'login.html')


def user(request):
    return render(request, 'user.html')


def add_user(request):
    return HttpResponse('添加用户成功!!!')


def role(request):
    return render(request, 'role.html')

login.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>login</title>
</head>
<body>
<form action="" method="post">
    {% csrf_token %}
    用户名:<input type="text" name="name">
    密码:<input type="password" name="pwd">
    <button type="submit">登录</button>
{#    <input type="submit">#}
</form>
</body>
</html>

为了测试登录功能,我们先使用createsuperuser 创建一个admin超级用户,密码为:admin1234,创建后就把我们创建的表添加进admin里面去

在rabc/admin.py添加这些代码即可:

from django.contrib import admin
from .models import *

# Register your models here.
admin.site.register(User)
admin.site.register(Role)
admin.site.register(Permission)

运行项目,你会发现,逐个用url打开,发现根本不需要权限即可访问,这样是不行的。
所以登录admin往里面添加我们需要的role, permission,user:
permission:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
role:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
添加完这些权限和对应的角色之后,就可以添加用户进行测试了:
user:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
这些操作完成后就该进行逻辑代码的编写了;

首先我们要在 app01/views.py下的login函数:

def login(request):
    if request.method == "POST":
        user = request.POST.get('name')
        pwd = request.POST.get('pwd')
        user_obj = User.objects.filter(name=user, pwd=pwd).first()
        if user_obj:
            # 把 user的id放入session
            request.session['user_id'] = user_obj.pk
            # 我们先把用户对应的拥有的url添加取出放到session中备用(通过user_obj跨表到role再到permission拿到url)
            role_obj = user_obj.roles.all()
            url_list = role_obj.values(
                'permissions__url')  # 取出的url是queryset对象 <QuerySet [{'permissions__url': '/user/'}, {'permissions__url': '/add/user/'}, {'permissions__url': '/role/'}]>
            permission_list = []
            for i in url_list:
                permission_list.append(i['permissions__url'])
            print(permission_list)
            request.session['permissions_list'] = permission_list
            return redirect('/user/')
        return render(request, 'login.html')

    return render(request, 'login.html')

这里面就进行了每个登录用户拥有的url权限进行记录保存了。

然后我们要判断用户是否有权限访问了:
app01/views.py下面的add_user:

def add_user(request):
    import re
    # 从session取出url列表
    permissions_list = request.session.get('permissions_list')
    print(permissions_list)
    # 取得当前的url用于和sesion中的进行比对
    current_url = request.path_info
    # 状态转换阀
    trance = False
    for i in permissions_list:
        # 进行前后限定。使其匹配准确度为百分百
        i = '^{0}$'.format(i)
        match = re.match(i, current_url)
        # 如果匹配成功,状态阀改为True,并跳出匹配
        if match:
            trance = True
            break
    # 根据状态阀来判断当前用户是否有访问权限
    if not trance:
        return HttpResponse('你没有权限访问!!!')

    return HttpResponse('添加用户成功!!!')

然后我们分别来尝试一下权限是否好用,首先使用001用户进行登录,访问,发现CEO的权限完全没问题,可以访问到添加用户页面,但是使用002 用户就不行咯
在这里插入图片描述
每写一个视图函数就需要重复这个代码,你可能会说使用装饰器呀,但是还是要每个函数添加一行代码呀,作为一个组件就是要够通用呢,我们还有一个很好的地方可以使用,那就是中间件,添加到中间件只需一次添加即可全局使用
所以我们需要把我们的代码添加到中间件。
我们写中间件参照session的中间件来写:
在这里插入图片描述
具体的中间件使用我就不解释了,改天再写一期中间件专场吧,毕竟中间件可是太强大了。这个设计。
回到我们代码:
我们首先在:rbac目录下创建rbac/my_code/permission.py里面编写:

# -*- coding: utf-8 -*-
# @Time    : 2019/12/16 下午 04:09
# @Author  : lh
# @Email   : [email protected]
# @File    : permission.py
# @Software: PyCharm
from django.shortcuts import HttpResponse
from django.utils.deprecation import MiddlewareMixin


class PermissionMiddleware(MiddlewareMixin):
    # 我们这边只用到process_requst未使用到process_response.所以我就没写response这个函数了,但是也不影响
    def process_request(self, request):
        import re
        # 从session取出url列表
        permissions_list = request.session.get('permissions_list', [])
        print(permissions_list)
        # 取得当前的url用于和sesion中的进行比对
        current_url = request.path_info
        # 状态转换阀
        trance = False
        for i in permissions_list:
            # 进行前后限定。使其匹配准确度为百分百
            i = '^{0}$'.format(i)
            match = re.match(i, current_url)
            # 如果匹配成功,状态阀改为True,并跳出匹配
            if match:
                trance = True
                break
        # 根据状态阀来判断当前用户是否有访问权限
        if not trance:
            return HttpResponse('你没有权限访问!!!')

在settings.py中添加我们写好的中间件:
在这里插入图片描述
这个中间件还有位置要求,因为我们中间件使用了session,所以他的位置必须在’django.contrib.sessions.middleware.SessionMiddleware’,的后面,但是我直接把它放在最后了。也没问题的。

但是还有一bug,那就是如果我们有一些页面是不需要权限就可以访问的,比如注册登录,还有admin一系列页面。所以我们还需要添加一个白名单才可以呢!
所以对rbac/my_code/permission.py进行更改:

# -*- coding: utf-8 -*-
# @Time    : 2019/12/16 下午 04:09
# @Author  : lh
# @Email   : [email protected]
# @File    : permission.py
# @Software: PyCharm

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


class PermissionMiddleware(MiddlewareMixin):
    # 我们这边只用到process_requst未使用到process_response.所以我就没写response这个函数了,但是也不影响
    def process_request(self, request):
        # 取得当前的url用于和sesion和白名单的进行比对
        current_url = request.path_info
        # 进行url白名单筛选
        white_url = ['/login/', '/register/', '/admin/.*']
        for url in white_url:
            my_url = re.match(url, current_url)
            if my_url:
                # 返回空就不会继续走后面的代码了
                return None

        # 从session取出url列表
        permissions_list = request.session.get('permissions_list', [])
        print(permissions_list)

        # 状态转换阀
        trance = False
        for i in permissions_list:
            # 进行前后限定。使其匹配准确度为百分百
            i = '^{0}$'.format(i)
            match = re.match(i, current_url)
            # 如果匹配成功,状态阀改为True,并跳出匹配
            if match:
                trance = True
                break
        # 根据状态阀来判断当前用户是否有访问权限
        if not trance:
            return HttpResponse('你没有权限访问!!!')

这样你想哪里url不需要权限添加进白名单即可了。

还有一个问题,那就是如果访问用户只是没有登录,你就不能直接说他们没有权限访问,而是要友好的跳转到登录页面。
所以在前面的代码继续添加几行即可:

# -*- coding: utf-8 -*-
# @Time    : 2019/12/16 下午 04:09
# @Author  : lh
# @Email   : [email protected]
# @File    : permission.py
# @Software: PyCharm

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


class PermissionMiddleware(MiddlewareMixin):
    # 我们这边只用到process_requst未使用到process_response.所以我就没写response这个函数了,但是也不影响
    def process_request(self, request):
        # 取得当前的url用于和sesion和白名单的进行比对
        current_url = request.path_info
        # 进行url白名单筛选
        white_url = ['/login/', '/register/', '/admin/.*']
        for url in white_url:
            my_url = re.match(url, current_url)
            if my_url:
                # 返回空就不会继续走后面的代码了
                return None
        # 判断用户是否登录使用前面session中的user_id
        user_id = request.session.get('user_id')
        if not user_id:
            return redirect('/login/')

        # 从session取出url列表
        permissions_list = request.session.get('permissions_list', [])

        # 状态转换阀
        trance = False
        for i in permissions_list:
            # 进行前后限定。使其匹配准确度为百分百
            i = '^{0}$'.format(i)
            match = re.match(i, current_url)
            # 如果匹配成功,状态阀改为True,并跳出匹配
            if match:
                trance = True
                break
        # 根据状态阀来判断当前用户是否有访问权限
        if not trance:
            return HttpResponse('你没有权限访问!!!')

既然是组件,我们需要把通用的代码给解耦处理才行
在这里插入图片描述
所以我们把它独立放在rbac/my_code/permission_url.py下面的initial_permission(request, user_obj)函数中,这样就独立出来。

这样一个基础的rbac组件就成型了。

发布了59 篇原创文章 · 获赞 18 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/weixin_38091140/article/details/103527200
今日推荐