一、权限管理rbac组件
1.权限管理组件的实现思路
表结构分析
rbac的意思之前我详细提过,就是基于角色的访问权限控制,其实说白了啊,就是针对不同的用户角色, 给他们分配了访问哪些url的权利,因为在实际工作场景中,不同分工的人之间的业务也应该是各自来展开的。
也就是说权限本质上是一个url访问路径,而在我们实现的rbac组件中,权限是分配到对应的角色下,然后角色和用户之间又是一层多对多的关系。
有人会问,既然你是想要给用户分配不同的权限,那么为什么不直接单独给用户来分配权限,而是要通过角色表来呢?其实这里涉及到的是一种编程思想。
- 如果我们用户数量很多,权限也很多,那么这么多用户,每一个用户都需要单独分配不同的权限,每个用户的权限也可能很多,这样在权限分配的时候会很繁琐。
- 而通过角色表,将不同角色的权限进行统一的划分,即时你用户再多,权限再多,角色数量总归不会太多。这个时候我们只需要第一次指定好权限和角色之间的对应关系,以后只需要关注用户和角色之间的关系,这样就大大的减少了权限分配业务量,而且整体的关系也更加明朗。
实现思路
- 访问权限
由于我们是要对每个用户的权限来限制,我们想一想什么地方能够做到呢?如果你django基础扎实,那么你很快就能想到中间件,中间件的process_request方法就是对所有过来的请求做一些全局的处理。
那么我们又该如何是每个用户的权限有区分呢?这种因角色不同而不同的数据肯定不可能通过全局来存储,肯定是将对应的权限信息存放在自己独有的空间里,这样的话我们就不难想到session,还记的session把?每个用户用来保存标识客户端的信息,其实session还可以存储其他的信息,比如我们这里的用户权限信息等等。
- 菜单权限
权限验证思路有了,现在的具体情况是如果某个用户有某个权限,我们让他顺利访问,如果他没有某个权限,但是他有这个url权限的链接显示,结果我们给人家展示了一个大黄页,你没有权限。想想都角色有点脑残是吧!用户体验极差,卷铺盖回家把。
那么这里改怎么处理呢,其实在给用户权限的时候,也应当分配好用户所拥有的菜单权限,也就是是够给用户展示的权限。具体的实现就是根据用户权限,来确定他对应拥有的菜单权限,从而在前端页面展示出来。
- 访问权限和菜单权限的注入
访问权限和菜单权限思路都有了,就涉及到什么时候注入了,我们知道权限是针对用户来的,那么这些权限的注入当然也是应该在用户登录成功的一瞬间就注入到这次会话的session中,这样大致的过程也就迎刃而解了。
2.前期页面准备
为了展示二级菜单效果,我们增加了一个私户展示页面,和公户页面同属于客户信息展示下的二级菜单。班级课程记录展示和学员学习记录展示属于教学信息展示页面下二级菜单。
同时为了显示没有子菜单的一级菜单效果,还增加了一个主页url权限,主页没有子菜单,而且主页所有人都可以访问。
主页url
我写在项目urls中,也就是ObCRM/urls.py中
from customer.views import customer
urlpatterns = [
# 主页url
url(r'^index/', customer.Index.as_view(),name="index"),
]
主页视图函数
主页视图我放在customer下views/customer.py中了。
# 主页
class Index(views.View):
@method_decorator(login_required) # 装饰器函数验证是否登录
def dispatch(self, request, *args, **kwargs):
res = super().dispatch(request, *args, **kwargs)
return res
def get(self,request):
# 展示主页
return render(request,"index.html")
主页html文件
这个主页我随便拷贝的模板,没有具体内容,继承的也是base页面,写在customer应用下templates中。
{% extends 'BASE.html' %}
{% block head %}
{
{ block.super }}
{% endblock head %}
{% block title %}
欢迎使用AliCRM系统
{% endblock title %}
{% block content %}
这是主页
{% endblock content %}
{% block js %}
{
{ block.super }}
{% endblock js %}
index.html
私户展示url
customer应用下urls中
# 私户数据展示
url(r'^private/list/', customer.PrivateList.as_view(), name="private_list"),
私户展示视图
私户展示我就简单实现了搜索,添加,编辑,删除等都是同样的,所以这里就不再实现了。
# 私户数据展示
class PrivateList(views.View):
@method_decorator(login_required)
def dispatch(self, request, *args, **kwargs):
res = super().dispatch(request, *args, **kwargs)
return res
def get(self, request):
condition = request.GET.get("condition", "")
query = request.GET.get("q", "")
condition = condition + "__contains"
q = Q() # Q实例化生成q对象,q对象可以帮我们拼接字符串为 condition__contians= xx的关键字参数传到filter中。
q.children.append((condition, query))
if condition and query: # 如果有查询调参数,两个参数都有,根据查询参数查询后找到数据
all_customers = models.Customer.objects.filter(q, consultant=request.user).order_by("-pk")
else: # 判断有没有查询参数,只有有一个没有参数,就查询所有公户数据
all_customers = models.Customer.objects.filter(consultant=request.user).order_by("-pk")
# 开始分页展示
data_counts = all_customers.count()
# 生成一个分页对象
paginator = Paginator(request, data_counts, 10)
# 获取当前页展示数据的范围
try: # 异常是否查到了数据,查到了才切片,不然会报错
all_customers = all_customers[paginator.start:paginator.end]
except Exception:
pass
# 获取分页的标签
paginator_tag = paginator.paginate() # 调用定义好的分页方法
# 获取跳转页的标签
jump_tag = paginator.jump_page() # 调用定义好的跳转页方法
return render(request, "private_list.html",
{
"all_customers": all_customers, "paginator_tag": paginator_tag, "jump_tag": jump_tag})
私户展示视图类
私户展示html
私户展示html,与公户区别不大。customer应用下templates中
{% extends 'BASE.html' %}
{% load static %}
{% block head %}
{
{ block.super }}
{% endblock head %}
{% block title %}
私户信息展示
{% endblock title %}
{% block content %}
<div class="row">
<div class="col-xs-12">
<div class="box">
<div class="box-header">
<h3 class="box-title"></h3>
<form action="" method="get" class="navbar-form navbar-left">
<div class="input-group">
<div class="input-group-btn btn-info">
<select name="condition" id="search" class="btn input-group-sm btn-info"
style="border: 0">
<option value="" readonly>条件</option>
<option value="qq_name">昵称</option>
<option value="qq">QQ号</option>
</select>
</div>
<input type="text" name="q" class="form-control" placeholder="Search...">
<span class="input-group-btn">
<button type="submit" id="search-btn" class="btn btn-flat">
<i class="fa fa-search"