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 ...
随机推荐
- mysql 8.0.12 创建新的数据库、用户并授权
Mysql安装成功后,默认的root用户密码为空,你可以使用以下命令来创建root用户的密码: [root@host]# mysqladmin -u root password "new_p ...
- 建立SQL链接服务器
访问链接服务器的格式:select * from [IPMLBZ].[数据库].[dbo].WEB_ItemInfo 有一个最简单的方法就是直接鼠标新建,这样是以ip为开头的,也可以通过下面的代码进行 ...
- jquery :checked(过滤选择器) 和 空格:checked(后代选择器)【转】
jquery 过滤选择器 和 后代选择器 <%@ page language="java" contentType="text/html; charset=UTF- ...
- Every-SG游戏
参考自 石家庄二中 贾志豪 IOI2009国家集训队论文 <组合游戏略述—— 浅谈 SG 游戏的若干拓展及变形> 一.定义 游戏规则加上 对于还没有结束的所有单一游戏,游戏者必须对其进行决 ...
- Codeforces 15 E. Triangles
http://codeforces.com/problemset/problem/15/E 题意: 从H点走下去,再走回H点,不能走重复路径,且路径不能把黑色三角形包围的方案数 中间的黑色三角形把整张 ...
- JAVA单链表的实现-不带头结点且没有尾指针
本程序采用JAVA语言实现了线性表的链式实现.首先定义了线性表的接口ListInterface,然后LList类实现了ListInterface完成了链表的实现. 本实现中,链表是不带表头结点的,且有 ...
- Github安全整理(转载)
刚好这两天对之前github上关注的一些比较有意思的项目进行了一下分类整理,在这里列出来分享给大家,希望能对大家寻找工具或者资源有所帮助. 大部分Repo是关于安全以及Python的,也有一些其他主题 ...
- [HAOI2018]奇怪的背包 (DP,数论)
[HAOI2018]奇怪的背包 \(solution:\) 首先,这一道题目的描述很像完全背包,但它所说的背包总重量是在模P意义下的,所以肯定会用到数论.我们先分析一下,每一个物品可以放无数次,可以达 ...
- jQuery - 字符串与json对象之间的转换
将字符串转换为json 在js中,我们是这样写的 var _data = eval('(' + data + ')'); 原理:eval() 函数可计算某个字符串,并执行其中的的 JavaScript ...
- ACM-ICPC 2018 徐州赛区网络预赛 G题
题目链接: https://nanti.jisuanke.com/t/31459 具体思路: 先顺序输入,然后回溯,假设已经加入了n个点,那么在加入的同时,首先看一下原先x轴上已经有过的点,找到第一个 ...