CRM,客户关系管理系统(Customer Relationship Management)。企业用CRM技术来管理与客户之间的关系,以求提高企业成功的管理方式,其目的是协助企业管理销售循环:新客户的招徕、保留旧客户、提供客户服务及进一步提高企业和客户的关系,并运用市场营销工具,提供创新式的我的化的客户商谈和服务,辅以相应的信息系统或信息技术如数据挖掘和数据库营销来协调全部公司与顾客间在销售、营销以及服务上的交互。css
此系统主要是以教育行业为背景,为公司开发的一套客户关系管理系统。考虑到各位童鞋可能处于各行各业,为了扩大的系统使用范围,特此将该项目开发改成组件化开发,让同窗们能够往后在本身公司快速搭建相似系统及新功能扩展。html
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
小伙子,告诉你一个事实,不经意间,你竟然设计出了一个经典的权限访问控制系统: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
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
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
《客户管理》系统截图:基本增删改查和Excel导入源码下载:猛击这里
以上简易版客户管理系统中的URL有:
那么接下来,咱们就在权限组件中录入相关信息:
这么一来,用户登陆时,就能够根据本身的【用户】找到全部的角色,再根据角色找到全部的权限,再将权限信息放入session,之后每次访问时候须要先去session检查是否有权访问。
已录入权限数据源码下载:猛击这里
含用户登陆权限源码下载:猛击这里(简易版)
含用户登陆权限源码下载:猛击这里
至此,基本的权限控制已经完成,基本流程为:
全部示例中的帐户信息:
帐户一: 用户名:alex 密码:123 帐户二: 用户名:wupeiqi 密码:123
本文参考连接:
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 - 获取当前用户的全部权限 - 权限校验
请下载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)
建立超级用户
python manage.py createsuperuser
登陆admin后台,开始录入数据
先增长用户,再增长角色,最后设置权限
注意,权限来自于urls.py
添加url完成以后,绑定角色和权限
这个做业,主要是能获得用户的受权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)
使用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+)/$']
注意:这里面的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 ]
在web目录下的views目录下,建立文件login.py
from django.shortcuts import render, redirect,HttpResponse def login(request): return render(request,"login.html")
在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)
在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>
在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>
在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) # 渲染错误页面
在中间件中,只要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 ]
测试登陆,访问页面
http://127.0.0.1:8000/login/
访问一个不存在url
完整代码,请查看github
https://github.com/987334176/luffy_permission/archive/v1.1.zip