python 全栈开发,Day107(CRM初始,权限组件之权限控制,权限系统表设计)
一、CRM初始
CRM,客户关系管理系统(Customer Relationship Management)。企业用CRM技术来管理与客户之间的关系,以求提升企业成功的管理方式,其目的是协助企业管理销售循环:新客户的招徕、保留旧客户、提供客户服务及进一步提升企业和客户的关系,并运用市场营销工具,提供创新式的个人化的客户商谈和服务,辅以相应的信息系统或信息技术如数据挖掘和数据库营销来协调所有公司与顾客间在销售、营销以及服务上的交互。
此系统主要是以教育行业为背景,为公司开发的一套客户关系管理系统。考虑到各位童鞋可能处于各行各业,为了扩大的系统使用范围,特此将该项目开发改为组件化开发,让同学们可以日后在自己公司快速搭建类似系统及新功能扩展。
- 权限系统,一个独立的rbac组件。
- stark组件,一个独立的curd组件。
- crm业务,以教育行业为背景并整合以上两个组件开发一套系统。
二、权限组件之权限控制
1. 问:为什么程序需要权限控制?
答:生活中的权限限制,① 看灾难片电影《2012》中富人和权贵有权登上诺亚方舟,穷苦老百姓只有等着灾难的来临;② 屌丝们,有没有想过为什么那些长得漂亮身材好的姑娘在你身边不存在呢?因为有钱人和漂亮姑娘都是珍贵稀有的,稀有的人在一起玩耍和解锁各种姿势。而你,无权拥有他们,只能自己玩自己了。
程序开发时的权限控制,对于不同用户使用系统时候就应该有不同的功能,如:
- 普通员工
- 部门主管
- 总监
- 总裁
所以,只要有不同角色的人员来使用系统,那么就肯定需要权限系统。
2. 问:为什么要开发权限组件?
答:假设你今年25岁,从今天开始写代码到80岁,每年写5个项目,那么你的一生就会写275个项目,保守估计其中应该有150+个都需要用到权限控制,为了以后不再重复的写代码,所以就开发一个权限组件以便之后55年的岁月中使用。 亲,不要太较真哦,你觉得程序员能到80岁么,哈哈哈哈哈哈哈
偷偷告诉你:老程序员开发速度快,其中一个原因是经验丰富,另外一个就是他自己保留了很多组件,新系统开发时,只需把组件拼凑起来基本就可以完成。
3. 问:web开发中权限指的是什么?
答:web程序是通过 url 的切换来查看不同的页面(功能),所以权限指的其实就是URL,对url控制就是对权限的控制。
结论:一个人有多少个权限就取决于他有多少个URL的访问权限。
权限表结构设计:第一版
问答环节中已得出权限就是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有:
- 客户管理
- 客户列表:/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
本文参考链接:
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完成之后,绑定角色和权限
相关权限表关系
表记录(大概)
测试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 = ''
# 查询表,用户名和密码是否匹配
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")
使用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)
在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
python 全栈开发,Day107(CRM初始,权限组件之权限控制,权限系统表设计)的更多相关文章
- python 全栈开发,Day110(django ModelForm,客户管理之 编辑权限(一))
昨日内容回顾 1. 简述权限管理的实现原理. 粒度控制到按钮级别的权限控制 - 用户登陆成功之后,将权限和菜单信息放入session - 每次请求时,在中间件中做权限校验 - inclusion_ta ...
- Python全栈开发记录_第十篇(反射及选课系统练习)
反射机制:反射就是通过字符串的形式,导入模块:通过字符串的形式,去模块中寻找指定函数,对其进行操作.也就是利用字符串的形式去对象(模块)中操作(查找or获取or删除or添加)成员,一种基于字符串的事件 ...
- python 全栈开发,Day72(昨日作业讲解,昨日内容回顾,Django多表创建)
昨日作业讲解 1.图书管理系统 实现功能:book单表的增删改查 1.1 新建一个项目bms,创建应用book.过程略... 1.2 手动创建static目录,并在目录里面创建css文件夹,修改set ...
- 巨蟒python全栈开发django8:基于对象和基于双下划线的多表查询
1.编辑删除&&多对多关系的其他方法 提交,数据,得到结果 查看运行 给编辑和删除,添加样式 我们点击删除,可以成功删除 打印sql语句的,在settings.py里边的配置 LOGG ...
- 巨蟒python全栈开发数据库攻略3:行记录的操作&单表查询3
1.数据行的增删改 2.单表查询 select&where条件 3.group by&having&order by&limit
- Python全栈开发【面向对象】
Python全栈开发[面向对象] 本节内容: 三大编程范式 面向对象设计与面向对象编程 类和对象 静态属性.类方法.静态方法 类组合 继承 多态 封装 三大编程范式 三大编程范式: 1.面向过程编程 ...
- python全栈开发中级班全程笔记(第二模块、第四章)(常用模块导入)
python全栈开发笔记第二模块 第四章 :常用模块(第二部分) 一.os 模块的 详解 1.os.getcwd() :得到当前工作目录,即当前python解释器所在目录路径 impor ...
- Python全栈开发【基础四】
Python全栈开发[基础四] 本节内容: 匿名函数(lambda) 函数式编程(map,filter,reduce) 文件处理 迭代器 三元表达式 列表解析与生成器表达式 生成器 匿名函数 lamb ...
- python 全栈开发之路 day1
python 全栈开发之路 day1 本节内容 计算机发展介绍 计算机硬件组成 计算机基本原理 计算机 计算机(computer)俗称电脑,是一种用于高速计算的电子计算机器,可以进行数值计算,又可 ...
- Win10构建Python全栈开发环境With WSL
目录 Win10构建Python全栈开发环境With WSL 启动WSL 总结 对<Dev on Windows with WSL>的补充 Win10构建Python全栈开发环境With ...
随机推荐
- Python介绍以及安装
Python介绍以及安装 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 借用我的导师的一句话:当你看到这篇文章的时候,那么恭喜你,你已经是踏入了开发的大门!欢迎加入:高级运维工程师 ...
- Python基础【day02】:元组和购物车练习的知识点
一.元组 元组其实跟列表差不多,也是存一组数,只不是它一旦创建,便不能再修改,所以又叫只读列表 用途:一般情况下用于自己写的程序能存下数据,但是又希望这些数据不会被改变,比如:数据库连接信息等 1.元 ...
- js正则表达式【原】
js正则表达式 http://www.w3school.com.cn/js/js_obj_regexp.asp js常用正则表达式 我的自测样例 <HTML> <HEAD> & ...
- Frame size of 257 MB larger than max allowed 100 MB
ActiveMQ有时会报类似Frame size of 257 MB larger than max allowed 100 MB的错误,意思是单条消息超过了预设的最大值,在配置文件中 <tra ...
- OpenGIS 介绍(转)
值此FOSS4G大会即将召开之日,最近我会在Blog上依次介绍一些OpenGIS标准.架构及用于实现的软件.一方面给初涉此行的朋友一个快速入门的概览,另一方面也是对我接触OpenGIS近一年来的总结. ...
- VS2013中修改MFC对话框左上角和exe图标
一.开发环境 1.VS2013: 2.C++ / MFC: 二.更改步骤 1)创建一个新工程,可以什么都不加.打开“资源视图”, 右键点击项目名称,选择“添加资源”,导入“Icon”资源文件(事先准备 ...
- Python - Scrapy 框架
Scrapy 是采用Python 开发的一个快速可扩展的抓取WEB 站点内容的爬虫框架.Scrapy,Python开发的一个快速,高层次的屏幕抓取和web抓取框架,用于抓取web站点并从页面中提取结构 ...
- 定时器QTimer
import sys from PyQt5.QtCore import QTimer, Qt from PyQt5.QtWidgets import QApplication, QWidget, QP ...
- REUSE_ALV_GRID_DISPLAY详解
CALL FUNCTION 'REUSE_ALV_GRID_DISPLAY' EXPORTING I_INTERFACE_CHECK = ' ' "接口一致性检查 * I_BYPASSING ...
- 第14月第23天 uitextfield文字下移
1. http://www.jianshu.com/p/641a0cbcabb0