设计Django权限
Django权限相关方法:
request.user下权限方法:
get_group_permissions(obj=None) 返回用户组权限的集合。 get_all_permissions(obj=None) 返回用户所有的权限集合。 has_perm(perm, obj=None) 用户是否具有某个权限。perm的格式是 "<app label>.<permission codename>". has_perms(perm_list, obj=None) 用户是否具有权限列表中的每个权限。
自定义Django权限
一、重写User表
1、继承AbstractBaseUser和PermissionsMixin
2、自定义Meta中的permissions对象
permissions=(
("crm_table_index","可以查看所有的Alexadmin的APP"),
("crm_table_list","可以查看每张表里所有的数据"),
("crm_table_list_view","可以访问表里每条数据的修改页"),
("crm_table_list_change","可以对表里的每条数据进行修改")
)
3、执行makemigrations和migrate后,确定在auth_permission表中已经自动写入了2中自定义的permissions
二、定义一个Model用于存放自定义更加权限
1、相比auth_permission表,权限颗粒度更细
2、可以包含GET,POST等request.method
3、可以包含GET访问字段,GET访问字段的值
4、可以自定义函数来实现各种自定义权限(数据库中将函数已字符串形式存储)
'crm_table_list':['model_table_list','GET',[],{'source':1},perm_custom_funcs.only_view_own_customers],#可以查看每张表里所有的数据
三、定义一个装饰器,用户Check View视图
def check_permission(func): """权限Check装饰器""" def wrapper(*args, **kwargs): request = args[0] if perm_check(*args, **kwargs): return func(*args, **kwargs) else: return render(request, "alexadmin/403.html") return wrapper
class CheckPermission(View):
"""重写View的dispath方法,让CBV继承该类"""
def dispatch(self, request, *args, **kwargs):
if not perm_check(request, *args, **kwargs):
return render(request, "alexadmin/403.html")
return super(CheckPermission, self).dispatch(request, *args, **kwargs)
四、自定义一个perm_check来对权限进行比较
1、通过request.user.get_all_permissions()方法获取用户所有权限(其实是从auth_permission表中获取)
2、如果权限为空,则直接返回403错误
3、将获取到的权限到自定义permissions数据中进行Filter出来
4、从request中获取GET,POST,URL等信息与第3步中Filter出来的详细信息进行对比
5、比较通过就放行,不通过就返回403页面
五、 相关代码
admin_permissions
perm_dict = { "crm_table_index": ["app_index", "GET", [], {}, ], # app_index是URL路由中每条url对应的name "crm_table_list": ["model_table_list", "GET", [], {"source": 1},], "crm_table_list_view": ["table_obj_change", "GET", ["source", "consultant"], {}], "crn_table_list_change": ["table_obj_change", "POST", ["source"], {}] }
permision_handle
from AlexAdmin import admin_permissions from django.core.urlresolvers import resolve from django.views import View from django.shortcuts import redirect, render from django.conf import settings def perm_check(request, *args, **kwargs): """ 1. 判断用户是否已登录 1.1 根据原生URL获取到相对的URL_name 1.2 根据URL_name循环permission_dict, 找到对应的权限条目 1.2.1 判断request.method 请求参数,是否都匹配 1.2.2 获取最终对应的权限条目 1.2.3 判断用户是否有这条权限 1.2.4 如果有,授权通过 1.2.5 没有权限,返回403错误 1.3 找不到对应权限条目,返回403错误 2. 没有登录,跳转到登录页面 3. 权限相关的方法 get_group_permissions(obj=None) 返回用户组权限的集合。 get_all_permissions(obj=None) 返回用户所有的权限集合。 has_perm(perm, obj=None) 用户是否具有某个权限。perm的格式是 "<app label>.<permission codename>". has_perms(perm_list, obj=None) 用户是否具有权限列表中的每个权限。 is_anonymous() 是否是匿名用户。 is_authenticated() 用户是否通过验证,登陆。 get_full_name() 返回first_name plus the last_name, with a space in between. get_short_name() 返回first_name. set_password(raw_password) 设置密码。 check_password(raw_password) 验证密码。 """ resolve_url_obj = resolve(request.path) print("result_url_obj======>", resolve_url_obj) # ResolverMatch(func=AlexAdmin.views.AppIndex, args=(), kwargs={}, url_name=app_index, app_names=[], namespaces=[]) current_url_name = resolve_url_obj.url_name print("current_url_name======>", current_url_name) # app_index match_key = None match_results = [None] if not request.user.is_authenticated(): return redirect(settings.LOGIN_URL) print(request.user.username) print("admin_permissions.perm_dict=====+>>>", admin_permissions.perm_dict ) for permission_key, permission_val in admin_permissions.perm_dict.items(): print("perm_check, permission_val============>", perm_check, permission_val) # 'crm_table_list': ['model_table_list', 'GET', [], {'source':1},perm_custom_funcs.only_view_own_customers] perm_url_name = permission_val[0] perm_method = permission_val[1] perm_args = permission_val[2] perm_kwargs = permission_val[3] custom_func_hook = None if len(permission_val) == 4 else permission_val[4] if perm_url_name == current_url_name: print("perm_url_name======>", perm_url_name) if perm_method == request.method: args_matched = False for item in perm_args: request_method_func = getattr(request, perm_method) # GET or POST if request_method_func.get(item, None): # 判断request 字典中是否有该参数 args_matched = True else: args_matched = False break # 只要有一个参数不能匹配成功,则判定为假,直接退出循环。 else: args_matched = True kwargs_matched = True for k, v in perm_kwargs.items(): request_method_func = getattr(request, perm_method) arg_val = request_method_func.get(k, None) # 判断request 字典中是否有该参数 if arg_val == str(v): # 匹配上了特定的参数及对应的参数值,比如request对象里必须有一个user_id=3的参数 kwargs_matched = True else: kwargs_matched = False break # 只要有一个参数不能匹配成功,则判定为假,直接退出循环。 else: kwargs_matched = True func_hook_passed = True if custom_func_hook: # 配置了自定义权限控制函数 func_res = custom_func_hook(*args, **kwargs) if func_res: func_hook_passed = True else: func_hook_passed = False match_results = [args_matched, kwargs_matched, func_hook_passed] print("match_results==========>", match_results) if all(match_results): match_key = permission_key break if all(match_results): app_name, *perm_name = match_key.split("_") perm_obj = "%s.%s" % (app_name, match_key) if request.user.has_perm(perm_obj): print("当前用户有此权限") return True else: return False else: print("未匹配到权限项, 当前用户无权限") class CheckPermission(View): def dispatch(self, request, *args, **kwargs): if not perm_check(request, *args, **kwargs): return render(request, "alexadmin/403.html") return super(CheckPermission, self).dispatch(request, *args, **kwargs)
View视图
class AppIndex(CheckPermission): def get(self, request): return render(request, "alexadmin/app_index.html", {"site": site})