权限管理项目笔记

创建项目

创建一个项目名为PermissionSystems

django-admin startproject PermissionSystems

进入项目目录,创建两个应用,一个专用权限管理的rbac,一个用于其他的app01

python manage.py startapp rbac

python manage.py startapp app01

rbac:基于角色的权限访问控制(Role-Based Access Control)

用Pycharm打开项目,注册app

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'rbac.apps.RbacConfig',
    'app01.apps.App01Config',
]

templates配置

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR, 'templates')]
        ,
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

设计rbac/models.py 数据库表

from django.db import models


class Permission(models.Model):
    """
    权限表
    """
    title = models.CharField(max_length=32)
    url = models.CharField(max_length=128)

    def __str__(self):
        return self.title


class Role(models.Model):
    """
    角色
    """
    title = models.CharField(max_length=32)
    permissions = models.ManyToManyField(to='Permission', blank=True)

    def __str__(self):
        return self.title


class UserInfo(models.Model):
    """
    用户表
    """
    name = models.CharField(max_length=32)
    password = models.CharField(max_length=64)
    email = models.CharField(max_length=32)
    roles = models.ManyToManyField(to='Role')

    def __str__(self):
        return self.name

生成数据库

python manage.py makemigrations

python manage.py migrate

注册rbac.admin.py用于用于插入数据

from django.contrib import admin
from . import models

admin.site.register(models.Permission)
admin.site.register(models.Role)
admin.site.register(models.UserInfo)

创建超级用户

python manage.py createsuperuser

用户名 superuser

密码 root123456

启动服务,进入admin后台插入数据

python manage.py runserver

访问http://127.0.0.1:8000/admin/

更改admin.py里的字段,显示两列

from django.contrib import admin

from . import models


class PermissionCongfig(admin.ModelAdmin):
    list_display = ['title', 'url']


admin.site.register(models.Permission, PermissionCongfig)

admin.site.register(models.Role)
admin.site.register(models.UserInfo)

权限表Permission

TITLE URL
编辑订单 /orders/edit/(\d+)/
删除订单 /orders/del/(\d+)/
添加订单 /orders/add/
订单列表 /orders/
编辑用户 /users/edit/(\d+)/
删除用户 /users/del/(\d+)/
添加用户 /users/add/
用户列表 /users/

添加角色

销售 订单列表、添加订单

销售主管 用户列表和所有订单操作

老板 所有操作权限

创建用户

User Role
王庆帅 老板
王庆酷 销售主管
王庆美 销售
王庆丑 销售

登录页面,筛选出登录用户的权限

urls.py

from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^login/', views.log_in),
]

app01/views.py

from django.shortcuts import render, HttpResponse
from rbac import models


def log_in(request):
    if request.method == "GET":
        return render(request, 'app01/login.html')
    if request.method == "POST":
        user = request.POST.get('user')
        pwd = request.POST.get('pwd')
        user = models.UserInfo.objects.filter(name=user, password=pwd).first()
        if user:
            # 根据当前用户找到已知的所有权限ORM跨表查询,过滤
            permission_list = user.roles.filter(permissions__title__isnull=False).values('permissions__title',
                                                                                         'permissions__url').distinct()
            for permission in permission_list:
                print(permission)
            return HttpResponse('登录成功')
        return render(request, 'app01/login.html')

templates/app01/login.html

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

这下可以筛选出对应的权限

比如王庆帅登录,筛选出:

{'permissions__title': '用户列表', 'permissions__url': '/users/'}
{'permissions__title': '添加用户', 'permissions__url': '/users/add/'}
{'permissions__title': '删除用户', 'permissions__url': '/users/del/(\\d+)/'}
{'permissions__title': '编辑用户', 'permissions__url': '/users/edit/(\\d+)/'}
{'permissions__title': '订单列表', 'permissions__url': '/orders/'}
{'permissions__title': '添加订单', 'permissions__url': '/orders/add/'}
{'permissions__title': '删除订单', 'permissions__url': '/orders/del/(\\d+)/'}
{'permissions__title': '编辑订单', 'permissions__url': '/orders/edit/(\\d+)/'}

上面的权限permissions__url数据应该加入session里。

from django.shortcuts import render, HttpResponse
from rbac import models


def log_in(request):
    if request.method == "GET":
        return render(request, 'app01/login.html')
    if request.method == "POST":
        user = request.POST.get('user')
        pwd = request.POST.get('pwd')
        user = models.UserInfo.objects.filter(name=user, password=pwd).first()
        if user:
            # 根据当前用户找到已知的所有权限ORM跨表查询,过滤
            permission_list = user.roles.filter(permissions__title__isnull=False).values('permissions__title',
                                                                                         'permissions__url').distinct()
            url_list = []
            for permission in permission_list:
                url_list.append(permission['permissions__url'])
            request.session['xxx'] = url_list
            return HttpResponse('登录成功')
        return render(request, 'app01/login.html')


def users(request):
    # 获取当前访问的URL : request.path_info

    print('当前访问的url', request.path_info)
    # 获取当前用户的session里的url
    url_list = request.session['xxx']
    for reg in url_list:
        print(reg)

    return HttpResponse('用户列表')

urls.py

from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^login/', views.log_in),
    url(r'^users/', views.users),
]

登录用户王庆帅,然后在访问http://127.0.0.1:8000/users/

结果

当前访问的url /users/
/users/
/users/add/
/users/del/(\d+)/
/users/edit/(\d+)/
/orders/
/orders/add/
/orders/del/(\d+)/
/orders/edit/(\d+)/

可以看到还有url里面有(\d+)的url,因此这里不应该用等号比较,而是用正则。

原理:

import re


current_url = '/uasdfasdfsers/'

url_list = [
    '/users/',
    '/users/del/(\d+)',
]

flag = False
for reg in url_list:
    if re.match(reg,current_url):
        flag = True
        break

if not flag:
    print('无权限访问')
else:
    print('有权限')

修改如下:

from django.shortcuts import render, HttpResponse
from rbac import models
import re


def log_in(request):
    if request.method == "GET":
        return render(request, 'app01/login.html')
    if request.method == "POST":
        user = request.POST.get('user')
        pwd = request.POST.get('pwd')
        user = models.UserInfo.objects.filter(name=user, password=pwd).first()
        if user:
            # 根据当前用户找到已知的所有权限ORM跨表查询,过滤
            permission_list = user.roles.filter(permissions__title__isnull=False).values('permissions__title',
                                                                                         'permissions__url').distinct()
            url_list = []
            for permission in permission_list:
                url_list.append(permission['permissions__url'])
            request.session['xxx'] = url_list
            return HttpResponse('登录成功')
        return render(request, 'app01/login.html')


def users(request):
    # 获取当前访问的URL : request.path_info
    # 获取当前用户的session里的url
    url_list = request.session['xxx']
    # 进行对比
    flag = False
    for reg in url_list:
        if re.match(reg, request.path_info):
            flag = True
    if not flag:
        return HttpResponse("无权访问")
    return HttpResponse('用户列表')

中间件的使用

上面这个是用户权限的认证。我们还有订单的认证。是不是还要在写一遍一样的方法?

此时我们想到了中间件!(而不是装饰器,装饰器也得都加上啊),对所有的请求做批量处理,用中间件是最简便的。

在rbac下创建service\init_permission.py用于初始化权限。

在rbac下创建middlewares\rbac.py中间件

并将中间件,session,白名单写到配置文件

service\init_permission.py

# -*- coding: utf-8 -*-
from django.conf import settings
from django.shortcuts import HttpResponse


def init_permission(user, request):
    """
    获取当前用户并放入到session中
    :param user:
    :param request:
    :return:
    """
    # 根据当前用户找到已知的所有权限ORM跨表查询,过滤
    permission_list = user.roles.filter(permissions__title__isnull=False).values('permissions__title',
                                                                                 'permissions__url').distinct()
    url_list = []
    for permission in permission_list:
        url_list.append(permission['permissions__url'])
    request.session[settings.PERMISSION_SESSION_KEY] = url_list

rbac.py

# -*- coding: utf-8 -*-
from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import render, HttpResponse
import re
from django.conf import settings  # 导入全局settings


class RbacMiddleware(MiddlewareMixin):
    def process_request(self, request):
        # 如果的登录界面,直接return None 通过此中间件
        # 获取白名单,让白名单中的所有url和当前访问url匹配
        for reg in settings.PERMISSION_VALID_URL:
            if re.match(reg, request.path_info):
                return None
        # 获取当前访问的URL : request.path_info
        # 获取当前用户的session里的url
        url_list = request.session.get(settings.PERMISSION_SESSION_KEY)
        # 获取权限
        if not url_list:
            return HttpResponse("为获取到当前用户的权限信息,无法访问")
        # 对用户请求的url进行匹配
        flag = False
        for reg in url_list:
            if re.match(reg, request.path_info):
                flag = True
        if not flag:
            return HttpResponse("无权访问")
        # 如果有权限不return 默认return None 继续往后走

app01/views.py其中from django.conf import settings导入的是全局的settings

from django.shortcuts import render, HttpResponse
from rbac import models
import re
from django.conf import settings
from rbac.service.init_permission import init_permission


def log_in(request):
    if request.method == "GET":
        return render(request, 'app01/login.html')
    if request.method == "POST":
        user = request.POST.get('user')
        pwd = request.POST.get('pwd')
        user = models.UserInfo.objects.filter(name=user, password=pwd).first()
        if user:
            # 初始化权限信息
            init_permission(user, request)
            return HttpResponse('登录成功')
        return render(request, 'app01/login.html')


def users(request):
    return HttpResponse("用户列表")


def orders(request):
    return HttpResponse("订单列表")

urls.py

from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^login/', views.log_in),
    url(r'^users/', views.users),
    url(r'^orders/', views.orders),
]

settings.py

注册中间件

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    'rbac.middlewares.rbac.RbacMiddleware',
]

权限相关配置

# ####################### 权限相关配置 ########################
PERMISSION_SESSION_KEY = "xxx"
PERMISSION_VALID_URL = [
    '/login/',
    '/admin/.*',
]

解决一个bug。进入admin.把角色销售的添加订单权限去掉。

写一个添加订单的函数,发现王庆丑也能访问,解决方案,给url加上起始符和终止符

rbac.py

# -*- coding: utf-8 -*-
from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import render, HttpResponse
import re
from django.conf import settings  # 导入全局settings


class RbacMiddleware(MiddlewareMixin):
    def process_request(self, request):
        # 如果的登录界面,直接return None 通过此中间件
        # 获取白名单,让白名单中的所有url和当前访问url匹配
        for reg in settings.PERMISSION_VALID_URL:
            if re.match(reg, request.path_info):
                return None
        # 获取当前访问的URL : request.path_info
        # 获取当前用户的session里的url
        url_list = request.session.get(settings.PERMISSION_SESSION_KEY)
        # 获取权限
        if not url_list:
            return HttpResponse("未获取到当前用户的权限信息,无法访问")
        # 对用户请求的url进行匹配
        flag = False
        for reg in url_list:
            reg = "^%s$" % (reg,)  # /orders/add/与/orders/是有区别的。加上起始符,终止符来进行约束。
            print(reg)
            if re.match(reg, request.path_info):
                flag = True
        if not flag:
            return HttpResponse("无权访问")
            # 如果有权限不return 默认return None 继续往后走

urls.py

from django.conf.urls import url
from django.contrib import admin
from app01 import views

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^login/$', views.log_in),
    url(r'^users/$', views.users),
    url(r'^orders/add/$', views.orders_add),
    url(r'^orders/$', views.orders),

]

更改数据库表,增加权限组

class PermissionGroup(models.Model):
    """
    权限组
    """
    caption = models.CharField(max_length=32)

    def __str__(self):
        return self.caption

1 用户组
2 订单组

权限与组直接建立关系


class Permission(models.Model):
    """
    权限表
    """
    title = models.CharField(max_length=32)
    url = models.CharField(max_length=128)
    group = models.ForeignKey(to='PermissionGroup',default=1)

    def __str__(self):
        return self.title

admin.py里再添加个字段

class PermissionCongfig(admin.ModelAdmin):
    list_display = ['title', 'url','group']

进入admin后台把订单改成订单组

再给Permission加一个code字段

class Permission(models.Model):
    """
    权限表
    """
    title = models.CharField(max_length=32)
    url = models.CharField(max_length=128)
    code = models.CharField(max_length=16, default='list')
    group = models.ForeignKey(to='PermissionGroup', default=1)

    def __str__(self):
        return self.title

code就是一个代号,给url一个代号用着方便

TITLE URL GROUP CODE
编辑订单 /orders/edit/(\d+)/ 订单组 edit
删除订单 /orders/del/(\d+)/ 订单组 del
添加订单 /orders/add/ 订单组 add
订单列表 /orders/ 订单组 list
编辑用户 /users/edit/(\d+)/ 用户组 edit
删除用户 /users/del/(\d+)/ 用户组 del
添加用户 /users/add/ 用户组 add
用户列表 /users/ 用户组 list

权限初始化

# -*- coding: utf-8 -*-
from django.conf import settings
from django.shortcuts import HttpResponse


def init_permission(user, request):
    """
    获取当前用户并放入到session中
    :param user:
    :param request:
    :return:
    """
    # 根据当前用户找到已知的所有权限ORM跨表查询,过滤
    permission_list = user.roles.filter(permissions__title__isnull=False).values('permissions__title',
                                                                                 'permissions__url',
                                                                                 'permissions__group_id',
                                                                                 'permissions__code').distinct()
    url_list = []
    for permission in permission_list:
        url_list.append(permission['permissions__url'])
    request.session[settings.PERMISSION_SESSION_KEY] = url_list

posted @ 2018-01-22 08:48  0bug  阅读(125)  评论(0)    收藏  举报