权限粒度控制到按钮
在上面的页面中,有添加缴费记录、删除缴费记录、修改缴费记录三个按钮,这三个按钮的权限不是对所有用户开放的,对与有权限的用户展示这些按钮是正常的,但是对于没有权限去操作按钮的用户,我们应该将无权限的按钮隐藏起来。就是说页面中的按钮显示与否,根据服务端权限配置来决定的,这就是权限粒度控制到按钮级别。
方式一:
我们之前在session中存储的权限信息结构是
[
{
'id': 1,
'url': '/customer/list/',
'pid': null
},
{
'id': 4,
'url': '/customer/edit/(?P<cid>\d+)/',
'pid': 1
}
]
我们在显示按钮时,拿到按钮对应的url,在权限列表中进行匹配,如果存在就显示,不存在就不显示。但是写起来麻烦,'/customer/edit/(?P<cid>\d+)/'
权限为这种时,就需要写正则来匹配。
方式二:
给每个url权限起个别名,后面拿着这个别名来来进行比较,就方便了很多,这个别名和在路由层的别名保持一致,在数据库中每个url对应的别名都是唯一且存在的。路由层的别名 url(r'^customer/list/$', customer.customer_list, name='customer_list'),
.
方式二的实现流程:
1.修改权限表结构,给添加别名字段,给已有的表增加新字段,先将这个字段的参数设置为null=True, blank=True
,进行数据库迁移和表字段内容更新。
class Permission(models.Model):
"""
权限表
"""
title = models.CharField(verbose_name='标题', max_length=32)
url = models.CharField(verbose_name='含正则的URL', max_length=128)
name = models.CharField(verbose_name='URL的别名', max_length=32, null=True, blank=True)
menu = models.ForeignKey(to="Menu", verbose_name="所属菜单", null=True, blank=True, help_text="null表示不是菜单,非null表示是二级菜单")
pid = models.ForeignKey(verbose_name="关联的权限",
help_text="对于非菜单权限选择一个可以成为菜单的权限,用于做默认展开和选中在访问非菜单功能时",
to="Permission",
null=True,
blank=True,
related_name="parents")
def __str__(self):
return self.title
2.数据更新完成后,将name字段修改为唯一不重复的属性,再次进行数据库迁移,name字段新增完毕。
name = models.CharField(verbose_name='URL的别名', max_length=32, unique=True)
3.存储在session中的权限信息结构修改。现在存储是以列表存储的,在判断当前按钮的url是否存在于session中时,不好进行判断,所以将以列表存储修改为以字典存储,用url的别名作为key,在判断的时直接判断key在不在session中,就可以决定了这个按钮是否显示。
4.代码修改
init_permission.py
def init_permission(current_user, request):
'''
初始化权限信息
:param current_user: 当前登录用户
:param request: 请求相关参数
:return:
'''
# 当前用户所有权限
permission_queryset = current_user.roles.filter(permissions__isnull=False).values(
"permissions__id",
"permissions__url",
"permissions__name",
"permissions__title",
"permissions__pid",
"permissions__pid__url",
"permissions__pid__title",
"permissions__menu__id",
"permissions__menu__title",
"permissions__menu__icon").distinct()
# 获取权限中的url + 菜单信息
menu_dict = {}
permission_dict = {}
for item in permission_queryset:
permission_dict[item.get('permissions__name')] = {
'id': item.get('permissions__id'),
'url': item.get('permissions__url'),
'pid': item.get('permissions__pid'),
'title': item.get('permissions__title'),
'p_title': item.get('permissions__pid__title'),
'p_url': item.get('permissions__pid__url')
}
menu_id = item.get('permissions__menu__id')
if not menu_id:
continue
node = {'title': item.get('permissions__title'), 'url': item.get("permissions__url"), 'id': item.get('permissions__id')}
if menu_id not in menu_dict.keys():
menu_dict[menu_id] = {
'title': item.get('permissions__menu__title'),
'icon': item.get('permissions__menu__icon'),
'children': [node]
}
else:
menu_dict[menu_id]['children'].append(node)
print(menu_dict)
# 放入session中
request.session[settings.PERMISSION_SESSION_KEY] = permission_dict
request.session[settings.MENU_SESSION_KEY] = menu_dict
mindwares/rbac.py
class RbacMiddleware(MiddlewareMixin):
def process_request(self, request):
'''
1. 拿到当前用户请求的url
2. 获取当前用户在session中保存的权限列表
3. 权限信息匹配
'''
current_url = request.path_info
# 有一些权限是所有人默认都有的,不需要做权限判断,先进行一个白名单判断,如果是白名单url,就不用再走权限判断了
valid_url_list = settings.VALID_URL_LIST
for valid_url in valid_url_list:
if re.match(valid_url, current_url):
# 白名单中的url,无需权限验证
# 返回None,继续走后续步骤
return None
permission_dict = request.session.get(settings.PERMISSION_SESSION_KEY)
if not permission_dict:
# 返回 HttpResponse 不走 后续步骤,直接返回到页面
return HttpResponse('为获取到用户权限信息')
print(current_url)
print(permission_dict)
flag = False
url_record = [
{'title': '首页', 'url': '#'}
]
for item in permission_dict.values():
regx = '^{}$'.format(item.get('url'))
if re.match(regx, current_url):
flag = True
request.current_selected_permission = item.get('pid') or item.get('id')
if item.get('pid'):
# 三级路径导航
url_record.extend([
{'title': item.get('p_title'), 'url': item.get('p_url')},
{'title': item.get('title'), 'url': item.get('url'), 'class': 'active'}
])
else:
url_record.extend([{'title': item.get('title'), 'url': item.get('url'), 'class': 'active'}])
print(url_record)
break
request.url_record = url_record
if not flag:
return HttpResponse('无权访问')
使用filter功能来判断,该按钮是否有权限显示
@register.filter
def has_permission(request, name):
"""
判断是否有权限
:param request:
:param name:
:return:
"""
if name in request.session.get(settings.PERMISSION_SESSION_KEY):
return True
return False
使用的地方:
filter只能接收两个参数,这是注意点。
{% load rbac %}
{% if request|has_permission:'customer_add' %}
<a class="btn btn-default" href="/customer/add/">
<i class="fa fa-plus-square" aria-hidden="true"></i> 添加客户
</a>
{% endif %}
5.在所有按钮处添加has_permission来判断按钮的显示即可