python 全栈开发,Day107(CRM初始,权限组件之权限控制,权限系统表设计)

 1、CRM初始

CRM,客户关系管理系统(Customer Relationship Management)。企业用CRM技术来管理与客户之间的关系,以求提高企业成功的管理方式,其目的是协助企业管理销售循环:新客户的招徕、保留旧客户、提供客户服务及进一步提高企业和客户的关系,并运用市场营销工具,提供创新式的我的化的客户商谈和服务,辅以相应的信息系统或信息技术如数据挖掘和数据库营销来协调全部公司与顾客间在销售、营销以及服务上的交互。css

此系统主要是以教育行业为背景,为公司开发的一套客户关系管理系统。考虑到各位童鞋可能处于各行各业,为了扩大的系统使用范围,特此将该项目开发改成组件化开发,让同窗们能够往后在本身公司快速搭建相似系统及新功能扩展。html

  • 权限系统,一个独立的rbac组件。
  • stark组件,一个独立的curd组件。
  • crm业务,以教育行业为背景并整合以上两个组件开发一套系统。

 

2、权限组件之权限控制

1. 问:为何程序须要权限控制?前端

   答:生活中的权限限制,① 看灾难片电影《2012》中富人和权贵有权登上诺亚方舟,穷苦老百姓只有等着灾难的来临;② 屌丝们,有没有想过为何那些长得漂亮身材好的姑娘在你身边不存在呢?由于有钱人和漂亮姑娘都是珍贵稀有的,稀有的人在一块儿玩耍和解锁各类姿式。而你,无权拥有他们,只能本身玩本身了。
程序开发时的权限控制,对于不一样用户使用系统时候就应该有不一样的功能,如:python

  • 普通员工
  • 部门主管
  • 总监
  • 总裁

因此,只要有不一样角色的人员来使用系统,那么就确定须要权限系统。jquery

2. 问:为何要开发权限组件?git

   答:假设你今年25岁,从今天开始写代码到80岁,每一年写5个项目,那么你的一辈子就会写275个项目,保守估计其中应该有150+个都须要用到权限控制,为了之后再也不重复的写代码,因此就开发一个权限组件以便以后55年的岁月中使用。 亲,不要太较真哦,你以为程序员能到80岁么,哈哈哈哈哈哈哈 
偷偷告诉你:老程序员开发速度快,其中一个缘由是经验丰富,另一个就是他本身保留了不少组件,新系统开发时,只需把组件拼凑起来基本就能够完成。程序员

3. 问:web开发中权限指的是什么?github

   答:web程序是经过 url 的切换来查看不一样的页面(功能),因此权限指的其实就是URL,对url控制就是对权限的控制。web

结论:一我的有多少个权限就取决于他有多少个URL的访问权限。ajax

权限表结构设计:初版

问答环节中已得出权限就是URL的结论,那么就能够开始设计表结构了。

  • 一个用户能够有多个权限。
  • 一个权限能够分配给多个用户。

你设计的表结构大概会是这个样子:

如今,此时此刻是否是以为本身设计出的表结构棒棒哒!!!

But,不管是是否认可,你仍是too young too native,由于老汉腚眼一看就有问题....

问题:假设 “老男孩”和“Alex” 这俩货都是老板,老板的权限必定是很是多。那么试想,若是给这俩货分配权限时须要在【用户权限关系表中】添加好多条数据。假设再次须要对老板的权限进行修改时,又须要在【用户权限关系表】中找到这俩人全部的数据进行更新,太他妈烦了吧!!! 相似的,若是给其余相同角色的人来分配权限时,必然会很是繁琐。

权限表结构设计:第二版

聪明机智的必定在上述的表述中看出了写门道,若是对用户进行角色的划分,而后对角色进行权限的分配,这不就迎刃而解了么。

  • 一我的能够有多个角色。
  • 一个角色能够有多我的。
  • 一个角色能够有多个权限。
  • 一个权限能够分配给多个角色。

表结构设计:

 此次调整以后,由原来的【基于用户的权限控制】转换成【基于角色的权限控制】,之后再进行分配权限时只须要给指定角色分配一次权限,给众多用户再次分配指定角色便可。

models.py 示例

from django.db import models


class Permission(models.Model):
    """
    权限表
    """
    title = models.CharField(verbose_name='标题', max_length=32)
    url = models.CharField(verbose_name='含正则的URL', max_length=128)

    def __str__(self):
        return self.title


class Role(models.Model):
    """
    角色
    """
    title = models.CharField(verbose_name='角色名称', max_length=32)
    permissions = models.ManyToManyField(verbose_name='拥有的全部权限', to='Permission', blank=True)

    def __str__(self):
        return self.title


class UserInfo(models.Model):
    """
    用户表
    """
    name = models.CharField(verbose_name='用户名', max_length=32)
    password = models.CharField(verbose_name='密码', max_length=64)
    email = models.CharField(verbose_name='邮箱', max_length=32)
    roles = models.ManyToManyField(verbose_name='拥有的全部角色', to='Role', blank=True)

    def __str__(self):
        return self.name
View Code

小伙子,告诉你一个事实,不经意间,你竟然设计出了一个经典的权限访问控制系统:rbac(Role-Based Access Control)基于角色的权限访问控制。你这么优秀,为何不来老男孩IT教育?路飞学城也行呀! 哈哈哈哈。

注意:如今的设计还不是最终版,但以后的设计都是在此版本基础上扩增的,为了让你们可以更好的理解,咱们暂且再此基础上继续开发,直到遇到没法知足的状况,再进行整改。

源码示例猛击下载

客户管理之权限控制

学习知识最好的方式就是试错,坑踩多了那么学到的知识天然而然就多了,因此接下里下来咱们用《客户管理》系统为示例,提出功能并实现,而且随着功能愈来愈多,一点点来找出问题,并解决问题。

目录结构:

luffy_permission/
├── db.sqlite3
├── luffy_permission
│   ├── __init__.py
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
├── manage.py
├── rbac            # 权限组件,便于之后应用到其余系统
│   ├── __init__.py
│   ├── admin.py
│   ├── apps.py
│   ├── models.py
│   ├── tests.py
│   └── views.py
├── templates
└── web            # 客户管理业务
    ├── __init__.py
    ├── admin.py
    ├── apps.py
    ├── models.py
    ├── tests.py
    └── views.py
View Code

rbac/models.py

from django.db import models


class Permission(models.Model):
    """
    权限表
    """
    title = models.CharField(verbose_name='标题', max_length=32)
    url = models.CharField(verbose_name='含正则的URL', max_length=128)

    def __str__(self):
        return self.title


class Role(models.Model):
    """
    角色
    """
    title = models.CharField(verbose_name='角色名称', max_length=32)
    permissions = models.ManyToManyField(verbose_name='拥有的全部权限', to='Permission', blank=True)

    def __str__(self):
        return self.title


class UserInfo(models.Model):
    """
    用户表
    """
    name = models.CharField(verbose_name='用户名', max_length=32)
    password = models.CharField(verbose_name='密码', max_length=64)
    email = models.CharField(verbose_name='邮箱', max_length=32)
    roles = models.ManyToManyField(verbose_name='拥有的全部角色', to='Role', blank=True)

    def __str__(self):
        return self.name

rbac/models.py
View Code

web/models.py

from django.db import models


class Customer(models.Model):
    """
    客户表
    """
    name = models.CharField(verbose_name='姓名', max_length=32)
    age = models.CharField(verbose_name='年龄', max_length=32)
    email = models.EmailField(verbose_name='邮箱', max_length=32)
    company = models.CharField(verbose_name='公司', max_length=32)


class Payment(models.Model):
    """
    付费记录
    """
    customer = models.ForeignKey(verbose_name='关联客户', to='Customer')
    money = models.IntegerField(verbose_name='付费金额')
    create_time = models.DateTimeField(verbose_name='付费时间', auto_now_add=True)

web/models.py
View Code

《客户管理》系统截图:基本增删改查和Excel导入源码下载猛击这里

以上简易版客户管理系统中的URL有:

  • 客户管理
    • 客户列表:/customer/list/
    • 添加客户:/customer/add/
    • 删除客户:/customer/list/(?P<cid>\d+)/
    • 修改客户:/customer/edit/(?P<cid>\d+)/
    • 批量导入:/customer/import/
    • 下载模板:/customer/tpl/
  • 帐单管理
    • 帐单列表:/payment/list/
    • 添加帐单:/payment/add/
    • 删除帐单:/payment/del/(?P<pid>\d+)/
    • 修改帐单:/payment/edit/<?P<pid>\d+/

那么接下来,咱们就在权限组件中录入相关信息:

  • 录入权限
  • 建立用户
  • 建立角色
  • 用户分配角色
  • 角色分配权限

这么一来,用户登陆时,就能够根据本身的【用户】找到全部的角色,再根据角色找到全部的权限,再将权限信息放入session,之后每次访问时候须要先去session检查是否有权访问。

已录入权限数据源码下载猛击这里

含用户登陆权限源码下载:猛击这里(简易版)

含用户登陆权限源码下载猛击这里

至此,基本的权限控制已经完成,基本流程为:

  • 用户登陆,获取权限信息并放入session
  • 用户访问,在中间件从session中获取用户权限信息,并进行权限验证。

全部示例中的帐户信息:

帐户一:
    用户名:alex
       密码:123
 
帐户二:
    用户名:wupeiqi
       密码:123
View Code

 

本文参考连接:

https://www.cnblogs.com/wupeiqi/articles/9178982.html

 

 

做业

1. django程序
2. 两个app 
    - rbac,权限相关全部的东西
        - models.py(三个类5张表)
    - web,随便写业务处理
        - models.py 

3. 找URL并使用django admin 录入到权限表
    urlpatterns = [
        url(r'^customer/list/$', customer.customer_list),
        url(r'^customer/add/$', customer.customer_add),
        url(r'^customer/edit/(?P<cid>\d+)/$', customer.customer_edit),
        url(r'^customer/del/(?P<cid>\d+)/$', customer.customer_del),
        url(r'^customer/import/$', customer.customer_import),
        url(r'^customer/tpl/$', customer.customer_tpl),

        url(r'^payment/list/$', payment.payment_list),
        url(r'^payment/add/$', payment.payment_add),
        url(r'^payment/edit/(?P<pid>\d+)/$', payment.payment_edit),
        url(r'^payment/del/(?P<pid>\d+)/$', payment.payment_del),
    ]
    
4. 角色和用户管理


5. 写代码 
    a. 用户登录 
        - 获取用户信息放入session 
        - 获取当前用户全部的权限并写入session 
    b. 编写中间件作权限信息校验
        - 获取当前请求URL
        - 获取当前用户的全部权限
        - 权限校验
View Code

请下载github代码

https://github.com/987334176/luffy_permission/archive/v1.0.zip

 

录入数据

修改rbac目录下的admin.py,注册表

from django.contrib import admin

# Register your models here.
from rbac import models

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

建立超级用户

python manage.py createsuperuser

登陆admin后台,开始录入数据

先增长用户,再增长角色,最后设置权限

注意,权限来自于urls.py

添加url完成以后,绑定角色和权限

 

相关权限表关系

 

表记录(大概)

 

测试ORM

这个做业,主要是能获得用户的受权url列表。若是这都不能查询出来,那么做业能够放弃了!

先不着急写页面,用脚本测试orm

修改rbac目录下的tests.py

from django.test import TestCase

# Create your tests here.
import os

if __name__ == "__main__":
    # 设置django环境
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "luffy_permission.settings")
    import django
    django.setup()

    from rbac import models

    # 固定用户名和密码
    user = 'xiao'
    pwd = '123'
    # 查询表,用户名和密码是否匹配
    obj = models.UserInfo.objects.filter(name=user, password=pwd).first()
    role = obj.roles.all()  # 查询当前用户的全部角色
    permissions_list = []  # 空列表,用来存放用户能访问的url列表

    for i in role:  # 循环角色
        per = i.permissions.all()  # 查看当前用户全部角色的全部权限
        # print(i.permissions.all())
        for j in per:
            # print(j.url)
            # 将全部受权的url添加到列表中
            permissions_list.append(j.url)

    print(permissions_list)
View Code

使用Pycharm执行输出:

['^customer/list/$', '^customer/add/$', '^customer/edit/(?P<cid>\\d+)/$', '^customer/del/(?P<cid>\\d+)/$', '^customer/import/$', '^customer/tpl/$', '^payment/list/$', '^payment/add/$', '^payment/edit/(?P<pid>\\d+)/$', '^payment/del/(?P<pid>\\d+)/$']
View Code

注意:这里面的url都是正则表达式!经过它来验证用户的url是否受权!

 

登陆页面

先作用户登陆

修改web目录下的urls.py,增长路径

from django.conf.urls import url
from web.views import customer,payment,auth,login

urlpatterns = [
    # 客户管理
    url(r'^customer/list/$', customer.customer_list),
    url(r'^customer/add/$', customer.customer_add),
    url(r'^customer/edit/(?P<cid>\d+)/$', customer.customer_edit),
    url(r'^customer/del/(?P<cid>\d+)/$', customer.customer_del),
    url(r'^customer/import/$', customer.customer_import),
    url(r'^customer/tpl/$', customer.customer_tpl),
    # 帐单管理
    url(r'^payment/list/$', payment.payment_list),
    url(r'^payment/add/$', payment.payment_add),
    url(r'^payment/edit/(?P<pid>\d+)/$', payment.payment_edit),
    url(r'^payment/del/(?P<pid>\d+)/$', payment.payment_del),
    # 登陆相关
    url(r'^$', login.login),  # 前端
    url(r'^login/$', login.login),
    url(r'^auth/$', auth.AuthView.as_view({'post': 'login'})),  # 认证api
]
View Code

在web目录下的views目录下,建立文件login.py

from django.shortcuts import render, redirect,HttpResponse

def login(request):
    return render(request,"login.html")
View Code

使用session

在web目录下的views目录下,建立文件auth.py

from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework.viewsets import ViewSetMixin
from rbac import models
from utils.response import BaseResponse

class AuthView(ViewSetMixin,APIView):
    authentication_classes = []  # 空列表表示不认证

    def login(self,request,*args,**kwargs):
        """
        用户登录认证
        :param request:
        :param args:
        :param kwargs:
        :return:
        """
        response = BaseResponse()  # 默认状态
        try:
            user = request.data.get('username')
            pwd = request.data.get('password')
            # print(user,pwd)
            # 验证用户和密码
            obj = models.UserInfo.objects.filter(name=user,password=pwd).first()
            if not obj:  # 判断查询结果
                response.code = 1002
                response.error = '用户名或密码错误'
            else:
                role = obj.roles.all()  # 查询当前用户的全部角色
                permissions_list = []  # 定义空列表

                for i in role:  # 循环角色
                    per = i.permissions.all()  # 查看当前用户全部角色的全部权限
                    # print(i.permissions.all())
                    for j in per:
                        # print(j.url)
                        # 将全部受权的url添加到列表中
                        permissions_list.append(j.url)

                # print(permissions_list)
                response.code = 1000

                # 增长session
                request.session['url'] = permissions_list

        except Exception as e:
            response.code = 10005
            response.error = '操做异常'

        # print(response.dict)
        return Response(response.dict)
View Code

 

在web目录下的templates目录下,建立login.html

{% load staticfiles %}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="{% static 'js/jquery-3.3.1.min.js' %} "></script>
    <script src="{% static 'plugins/sweetalert/sweetalert-dev.js' %} "></script>
    <link rel="stylesheet" href="{% static 'plugins/sweetalert/sweetalert.css' %}">
</head>
<body>
<div style="height: 100px;"></div>
<form action="/auth/" method="post">
    <lable>用户名</lable>
    <input type="text" name="username" id="user">
    <lable>密码</lable>
    <input type="password" name="password" id="pwd">
    <input type="button" id="sub" value="登陆">
</form>
<script>
    $("#sub").click(function () {
        $.ajax({
            url: "/auth/",
            type: "post",
            data: {
                username: $("#user").val(),
                password: $("#pwd").val(),
            },
            success: function (data) {
                console.log(data);
                if (data.code == 1000) { //判断json的状态
                    swal({
                        title: '登陆成功',
                        type: 'success',  //展现成功的图片
                        timer: 500,  //延时500毫秒
                        showConfirmButton: false  //关闭确认框
                    }, function () {
                        window.location.href = "/customer/list/";  //跳转
                    });
                } else {
                    swal("登陆失败!", data.error,
                        "error");
                    {#window.location = "/backend/add_category/";#}
                }
            },
            error: function (data) {
                console.log('登陆异常');
            }
        })

    });

</script>
</body>
</html>
View Code

在web目录下的templates目录下,建立error.html

{% load staticfiles %}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<script src="{% static 'js/jquery-3.3.1.min.js' %} "></script>
{#<link rel="stylesheet" href="http://mishengqiang.com/sweetalert/css/sweetalert.css">#}
<link rel="stylesheet" href="{% static 'plugins/sweetalert/sweetalert.css' %}">
{#<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>#}
<script src="{% static 'js/jquery-3.3.1.min.js' %}"></script>
{#<script src="http://mishengqiang.com/sweetalert/js/sweetalert-dev.js"></script>#}
<script src="{% static 'plugins/sweetalert/sweetalert-dev.js' %}"></script>
<div>
    {#获取错误信息#}
    <input type="hidden" id="msg" value="{{ msg }}">
    <input type="hidden" id="url" value="{{ url }}">
</div>

<script>
    $(function () {
        var msg = $("#msg").val();
        var url = $("#url").val();
        console.log(msg);
        console.log(url);

        if (msg.length > 0) {  //判断是否有错误信息
            swal({
                title: msg,
                text: "1秒后自动关闭。",
                type: 'error',
                timer: 1000,
                showConfirmButton: false
            }, function () {
                window.location.href = url;  //跳转指定url
            });

        }


    })
</script>

</body>
</html>
View Code

 

中间件验证

在web目录下建立文件middlewares.py

from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import render, redirect, HttpResponse
from luffy_permission import settings
import os

class AuthMD(MiddlewareMixin):  # 验证登陆
    white_list = ['/','/login/','/auth/','/admin/' ]  # 白名单
    # black_list = ['/black/', ]  # 黑名单
    ret = {"status": 0, 'url': '', 'msg': ''}  # 默认状态

    def process_request(self, request):  # 请求以前
        request_url = request.path_info  # 获取请求路径
        # get_full_path()表示带参数的路径
        # print(request.path_info, request.get_full_path())

        # 判断请求路径不在白名单中
        if request_url not in self.white_list:
            import re
            per_url = request.session.get("url")  # 获取用户session中的url列表
            # print(per_url)
            if per_url:
                for i in per_url:  # 循环url列表
                    # 使用正则匹配。其中i为正则表达式,request_url.lstrip('/')表示去除左边的'/'
                    result = re.match(i, request_url.lstrip('/'))
                    # print(result)
                    if result:  # 判断匹配结果
                        print('受权经过',request_url)
                        return None  # return None表示能够继续走下面的流程
                    # else:
                    #     print('受权不经过',request_url)
                    #     # return redirect('/login/')
    

                # 错误页面提示
                self.ret['msg'] = "未受权,禁止访问!"
                self.ret['url'] = "/login/"
                path = os.path.join(settings.BASE_DIR, 'web/templates/error.html'),
                return render(request, path, self.ret)  # 渲染错误页面
View Code

在中间件中,只要return none,就会进入下一个中间件!

修改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',
    'web.middlewares.AuthMD',  # 自定义中间件AuthMD
]
View Code

 

测试登陆,访问页面

http://127.0.0.1:8000/login/

访问一个不存在url

完整代码,请查看github

https://github.com/987334176/luffy_permission/archive/v1.1.zip

相关文章
相关标签/搜索