Django 基于角色的权限控制
ENV: Python3.6 + django1.11
应用场景
有一种场景, 要求为用户赋予一个角色, 基于角色(比如后管理员,总编, 编辑), 用户拥有相应的权限(比如管理员拥有所有权限, 总编可以增删改查, 编辑只能增改, 有些页面的按钮也只有某些角色才能查看), 角色可以任意添加, 每个角色的权限也可以任意设置
django 的权限系统
django 默认的权限是基于Model的add, change, delete来做权限判断的, 这种设计方式有一个明显的缺陷, 比如怎么控制对Model的某个字段的修改的权限的控制呢
设计权限
大多数的系统, 都会给用户赋予某个角色, 假如能针对用户的角色, 做权限控制,这个权限控制并不局限于对Model的修改, 可以是任意位置的权限控制, 只要有一个权限名, 即可根据用户角色名下是否拥有权限名判断是否拥有权限
User, Role => UserRole => RolePermissions
User 是用户对象
Role: 角色表
UserRole 是用户角色关系对象
RolePermissions 是角色权限关系对象
因此, 需要创建三个Model: User,Role, UserRole, RolePermission
User 可以使用django 默认的User 对象
其他Model 如下
class Role(models.Model):
"""角色表"""
# e.g add_user
role_code = models.CharField('role code', max_length=64, unique=True, help_text = '用户角色标识')
# e.g 新增用户
role_name = models.CharField('role name', max_length=64, help_text = '用户角色名') class UserRole(models.Model):
"""用户角色关系表"""
user_id = models.IntegerField('user id', blank=False, help_text='用户id', unique=True)
role_codes = models.CharField('role codes', blank=True, default=None, max_length=256, help_text='用户的角色codes') class RolePermission(models.Model):
"""角色权限关系表"""
role_code = models.CharField('role code', max_length=64, blank=False, help_text = '用户角色标识')
pm_code = models.CharField('permission code', blank=False, max_length=64, help_text='权限code') class Meta:
unique_together = ('role_code', 'pms_code')
其中 Role 和 RolePermission 用于管理角色和对应的权限的关系
UserRole 用于管理用户和角色的映射关系
权限管理
用户角色拥有哪些权限是在代码里定义好的, 比如:
PMS_MAP = (
('PM_ADD_USER', '新增用户'),
('PM_SET_MAIL', '编辑邮箱'),
...
)
PM_ADD_USER 是权限code码, 新增用户 是权限名, 在这里, 权限名由我们定义, 后面在需要使用的地方做has_perm(<pm_coede>) 判断时, 用的就是这是这个code
角色管理
在定义好权限后, 我们就可以做角色管理了,
在这里, 我们可以创建任意的角色, 为其分配任意的权限, 当然, 最好创建有意义的角色
角色表单定义(forms.py)
role_regex_validator = RegexValidator(r"[a-zA-Z0-9]", "角色标记只能包含字母,数字, 下划线")
class RoleForm(forms.Form):
role_row_code = forms.IntegerField(required=False, widget=forms.HiddenInput())
role_code = forms.CharField(label='角色标记', min_length=3, max_length=64, validators=[role_regex_validator])
role_name = forms.CharField(label='角色名', min_length=3, max_length=64)
OPTIONS = PMS_MAP
pms = forms.MultipleChoiceField(label='权限列表', widget=forms.SelectMultiple(choices=OPTIONS)
角色编辑views.py
def role_edit(request):
"""角色编辑"""
if request.method == 'POST':
role_row_id = request.POST.get('role_row_id', 0)
role_code = request.POST.get('role_code', '')
role_name = request.POST.get('role_name', '')
pms = request.POST.getlist('pms', []) # 表单校验
role_form = RoleForm({
'role_row_id': role_row_id,
'role_code': role_code,
'role_name': role_name,
'pms': pms
})
# 表单校验
if not role_form.is_valid():
return render(request, 'role_form.html', {'form': role_form) role_row_id = role_form.cleaned_data.get('role_row_id', None)
if role_row_id:
# 角色更新
return update_role(request, role_form, role_row_id=role_row_id, role_code=role_code,
role_name=role_name, pms=pms)
else:
# 角色创建
return add_role(request, role_form, role_code, role_name, pms=pms) else:
# 角色编辑页面
role_row_id = request.GET.get('id')
try:
role_item = Role.objects.get(pk=role_row_id)
except Role.DoesNotExist as e:
role_item = None
if role_item:
# 编辑已有角色表单
# 获取角色权限列表
role_pms_rows = RolePermission.objects.filter(role_code=role_item.role_code)
pms_codes = [role_pms_row.pms_code for role_pms_row in role_pms_rows] role_form = RoleForm({
'role_row_id': role_row_id,
'role_code': role_item.role_code,
'role_name': role_item.role_name,
'pms': pms_codes
})
else:
# 新增角色表单
role_form = RoleForm() return render(request, 'role_form.html', {'form': role_form}) def add_role(request, role_form, role_code, role_name, pms=()):
"""新增角色"""
try:
with transaction.atomic():
role_item = Role.objects.create(role_code=role_code, role_name=role_name)
for pm_code in pms:
RolePermission.objects.update_or_create(role_code=role_code, pms_code=pm_code)
return redirect('{}?id={}'.format(reverse('user_role_edit'), role_item.pk))
except IntegrityError as e:
# 创建出错
role_form.add_error('role_code', '角色已经存在: {}'.format(role_code))
return render(request, 'role_form.html', {'form': role_form}) def update_role(request, role_form, role_row_id, role_code, role_name, pms=()):
"""更新角色""" try:
with transaction.atomic():
role_item = Role.objects.get(pk=role_row_id)
# 校验合法性
if not role_item:
raise Http404('非法的role记录id')
role_item.role_name = role_name
role_item.save() # 删除原角色权限设置
RolePermission.objects.filter(role_code=role_code).delete() for pm_code in pms:
RolePermission.objects.update_or_create(role_code=role_code, pms_code=pm_code)
return redirect('{}?id={}'.format(reverse('user_role_edit'), role_row_id))
except IntegrityError as e:
# 更新出错
role_form.add_error('role_name', '更新角色名出错:{}'.format(role_name))
return render(request, 'role_form.html', {'form': role_form})
表单部分html
<form class='form-horizontal' action='/user/role/edit' method='POST'>
<p>用户角色编辑</p>
{{form.non_field_errors}}
{% csrf_token %}
{% for hidden_field in form.hidden_fields %}
{{ hidden_field }}
{% endfor %} {% for field in form.visible_fields %}
<div class='form-group'>
<label class='col-lg-2 control-label'>{{field.label}}</label>
{% if field.errors %}
<div class='col-lg-3 has-error'>
{{field}}
{% for error in field.errors %}
<p><span class='help-block m-b-none'>{{error}}</span><p>
{% endfor %}
</div>
{% else %}
<div class='col-lg-3'>
{{field}}
</div>
{% endif%}
</div>
{% endfor %}
<div class="form-group">
<div class="col-lg-offset-2 col-lg-10">
<button class="btn btn-sm btn-white" type="submit">Save</button>
</div>
</div>
</form>
至此用户角色编辑就完成了
还有一部分是用户角色分配
说白了就是编辑用户时, 给用户选择一个角色, 将用户id和角色code 通过UserRole存到数据库中, 这一部分请各位自己实现吧 :)
权限判断
如果我们有了一个用户, 并赋予了一个角色, 应该怎么判断其是否有某个权限呢
在django的认证体系配置里, 有一项配置是AUTHENTICATION_BACKENDS, 比如, 我们希望对接sso 单点登录, 就可以在这里添加配置
AUTHENTICATION_BACKENDS = (
'django.contrib.auth.backends.ModelBackend',
'cas.backends.CASBackend', # 单点登录实现
)
这个配置项下每个字符串都对应一个类, 每个类继承并了一组接口实现中的全部或其中一部分
这组接口定义了用户认证和授权的相关内容, 如下
authenticate
get_user_permissions
get_group_permissions
has_perm
...
其中 has_perm 就是直接判断是否拥有某个权限的接口
在 AUTHENTICATION_BACKENDS中, 只要有一个类的 has_perm 判定用户拥有某个权限即可认为用户拥有该权限
因此, 我们实现自己的has_perm 实现
class PermBackend(ModelBackend):
def has_perm(self, user_obj, pms_code, obj):
if not user_obj.is_active:
return False
# 超级管理员拥有所有权限
if user_obj.is_superuser:
return True try:
user_roles_record = UserRole.objects.get(user_id=user_obj.pk)
except UserRole.DoesNotExist as e:
return False # 获取用户的角色(暂时是单个角色)
user_roles = user_roles_record.role_codes.split(',')
# 角色对应的权限集合
role_pms_rows = RolePermission.objects.filter(role_code__in=user_roles) pms_codes = [role_pms_row.pms_code for role_pms_row in role_pms_rows] # pms_code 是否在用户的权限code集合中
return pms_code in pms_codes
当然, 别忘了把PermBackend 放到 AUTHENTICATION_BACKENDS 最后
现在, 我们在views funcion的实现前添加 @permission_required(<pms_code>) 装饰器就能根据当前用户的角色, 判定是否拥有某个<pms_code> 权限啦
无论是任何地方, 只要定义唯一的pms_code, 赋给角色, 并将角色分配给某个用户, 就可以实现粒度很深的权限控制, 在本文开始的地方的所说的对某个Model单字段的修改权限也就不在话下了
Django 基于角色的权限控制的更多相关文章
- webapi框架搭建-安全机制(四)-可配置的基于角色的权限控制
webapi框架搭建系列博客 在上一篇的webapi框架搭建-安全机制(三)-简单的基于角色的权限控制,某个角色拥有哪些接口的权限是用硬编码的方式写在接口上的,如RBAuthorize(Roles = ...
- webapi框架搭建-安全机制(三)-简单的基于角色的权限控制
webapi框架搭建系列博客 上一篇已经完成了“身份验证”,如果只是想简单的实现基于角色的权限管理,我们基本上不用写代码,微软已经提供了authorize特性,直接用就行. Authorize特性的使 ...
- 图文详解基于角色的权限控制模型RBAC
我们开发一个系统,必然面临权限控制的问题,即不同的用户具有不同的访问.操作.数据权限.形成理论的权限控制模型有:自主访问控制(DAC: Discretionary Access Control).强制 ...
- RBAC: K8s基于角色的权限控制
文章目录 RBAC: K8s基于角色的权限控制 ServiceAccount.Role.RoleBinding Step 1:创建一个ServiceAccount,指定namespace Step 2 ...
- thinkphp基于角色的权限控制详解
一.什么是RBAC 基于角色的访问控制(Role-Based Access Control)作为传统访问控制(自主访问,强制访问)的有前景的代替受到广泛的关注. 在RBAC中,权限与角色相关联,用户通 ...
- IdentityServer4实战 - 基于角色的权限控制及Claim详解
一.前言 大家好,许久没有更新博客了,最近从重庆来到了成都,换了个工作环境,前面都比较忙没有什么时间,这次趁着清明假期有时间,又可以分享一些知识给大家.在QQ群里有许多人都问过IdentityServ ...
- [Kubernetes]基于角色的权限控制之RBAC
Kubernetes中有很多种内置的编排对象,此外还可以自定义API资源类型和控制器的编写方式.那么,我能不能自己写一个编排对象呢?答案是肯定的.而这,也正是Kubernetes项目最具吸引力的地方. ...
- 【Kubernetes】基于角色的权限控制:RBAC
Kubernetes中所有的API对象,都保存在Etcd里,对这些API对象的操作,一定都是通过访问kube-apiserver实现的,原因是需要APIServer来做授权工作. 在Kubernete ...
- RBAC - 基于角色的权限控制
ThinkPHP中关于RBAC使用详解 自己的源码下载:百度网盘,thinkPHP文件夹下,RBAC文件夹. 重要的是,权限信息的写入函数等.在源码中能找到,Modules/Amin/Common/c ...
随机推荐
- PAT Basic 1039 到底买不买 (20 分)
小红想买些珠子做一串自己喜欢的珠串.卖珠子的摊主有很多串五颜六色的珠串,但是不肯把任何一串拆散了卖.于是小红要你帮忙判断一下,某串珠子里是否包含了全部自己想要的珠子?如果是,那么告诉她有多少多余的珠子 ...
- Codeforces 899 1-N两非空集合最小差 末尾最多9对数计算 pair/链表加优先队列最少次数清空
A /*Huyyt*/ #include<bits/stdc++.h> #define mem(a,b) memset(a,b,sizeof(a)) #define pb push_bac ...
- 代码管理工具 Git
之前一直使用微软的代码管理工具TFS(Team Foundation Server)..NET CORE 2.0的发布后,考虑到.NET CORE项目可以跨平台,准备把项目迁移到.NET CORE 环 ...
- web.xml中url-pattern中/和/*的区别(来自网络)
其中/和/*的区别: < url-pattern > / </ url-pattern > 不会匹配到*.jsp,即:*.jsp不会进入spring的 Dispatcher ...
- SugarCRM开发入门
SugarCRM官网下载地址:https://sourceforge.net/projects/sugarcrm/ 概述 Sugar最初是基于LAMP(Linux.Apache.MySQL和PHP)运 ...
- 【HDU5289】Assignment
题目大意:给定一个长度为 N 的序列,求序列中最大值和最小值相差小于 K 的连续段的个数. 题解: 最大值和最小值相差不超过 K 是一个在值域角度的限制,应考虑采用平衡树或权值...数据结构进行维护. ...
- 第二篇:请求库之requests,selenium
requests模块 一.介绍 #介绍:使用requests可以模拟浏览器的请求,比起之前用到的urllib,requests模块的api更加便捷(本质就是封装了urllib3) #注意:reques ...
- eclipse修改代码后都需要clean的解决办法
问题描述: 用STS(类似于Eclipse)正在开发一个JavaWeb项目,但不知怎么的有一天,修改完Java代码,点击运行Tomcat,发现根本没有修改.刚刚开始的时候,因为一开始没找到原因而且工期 ...
- mysql——InnoDB 锁
https://www.cnblogs.com/leedaily/p/8378779.html 1.InnoDB锁的实现方式:给索引项加锁,只有通过索引条件检索数据,InnoDB才使用行级锁,否则,I ...
- LDA主题模型评估方法–Perplexity
在LDA主题模型之后,需要对模型的好坏进行评估,以此依据,判断改进的参数或者算法的建模能力. Blei先生在论文<Latent Dirichlet Allocation>实验中用的是Per ...