zoukankan      html  css  js  c++  java
  • 通用权限框架

    Web 开发中权限管理必不可少,不同的角色有不同的权限,所谓权限也可以理解为是否能访问某些特定页面。

    下面我们来实现一个通用权限框架:

    • 以配置文件的方式来配置权限
    • 以装饰器形式来给视图函数添加权限管理

    模型设计

    用户表采用拓展 auth 模块方式,不用原有的 auth_user 表,而是自己创建一个 UserProfile 表,它在 auth 模块基础上进行拓展,同时赋予权限操作。

    from django.db import models
    from django.contrib.auth.models import User, AbstractUser   # 导入 AbstractUser 类
    from django.contrib.auth.models import (
        BaseUserManager, AbstractBaseUser, PermissionsMixin			# 权限
    )
    
    
    class UserProfileManager(BaseUserManager):
        def create_user(self, email, name, password=None):
            """创建普通账户"""
            if not email:
                raise ValueError('Users must have an email address')
    
            user = self.model(
                email=self.normalize_email(email),
                name=name,
            )
    
            user.set_password(password)		# 	set_password():auth 模块中方法,用于修改密码,将密码 md5 加密,具体用法可参考 Django auth 模块		
            user.save(using=self._db)		# 保存
            return user
    
        def create_superuser(self, email, name, password):
            """创建超级用户"""
            user = self.create_user(
                email,
                password=password,
                name=name,
            )
            user.is_superuser = True
            user.save(using=self._db)
            return user
    
    
    class UserProfile(AbstractBaseUser, PermissionsMixin):
        email = models.EmailField(
            verbose_name='邮箱',
            max_length=255,
            unique=True,
            default='hj@qq.com',
        )
        name = models.CharField(max_length=64, verbose_name='姓名')
        is_active = models.BooleanField(default=True)
        is_staff = models.BooleanField(default=True)
        detail = models.OneToOneField('UserDetail', verbose_name='员工信息', on_delete=models.CASCADE, blank=True, null=True)
        role = models.ManyToManyField('Role', blank=True)
    
        objects = UserProfileManager()
    
        USERNAME_FIELD = 'email'
        REQUIRED_FIELDS = ['name']
    
        def __str__(self):
            return self.get_username()
    
        def get_full_name(self):
            # The user is identified by their email address
            return self.email
    
        def get_short_name(self):
            # The user is identified by their email address
            return self.email
    
        class Meta:
            verbose_name_plural = '用户表'
            # 配置权限
            permissions = (
                ('app_staff_detail', '可以查看员工个人信息的数据'),
                ('app_fina_state', '可以查看财务报表'),
                ('app_payroll', '可以查看工资条'),
            )
    

    另外在生成 UserProfile 表之前,需要在 settings 中指定路径:

    AUTH_USER_MODEL = 'app.UserProfile'
    

    使用

    配置权限列表 permission_dict

    # 键:app 名字 + url_name
    # 值:请求 URL 地址,请求方法(get、post),[]、{} 皆为过滤条件
    from app.my_primission import permission_hook
    
    # app 名字_(url_name)
    perm_dic = {
    	# 'app_table_list': ['table_obj_list', 'GET', [], {}, permission_hook.view_my_own_customers], 
        'app_staff_detail': ['staff_detail', 'GET', [], {}],  # 可以查看员工个人信息
        'app_fina_state': ['fina_state', 'GET', [], {}],  # 可以查看财务报表
        'app_payroll': ['payroll', 'GET', [], {}],  # 可以查看工资条
    
    }
    

    核心部分 my_primission.py

    from django.urls import resolve
    from django.shortcuts import render,redirect,HttpResponse
    from kingadmin.permission_list import perm_dic
    from django.conf import settings
    
    
    def perm_check(*args, **kwargs):
        #1.获取当前请求的url
        #2.把url解析成url_name(通过resolve)
        #3.判断用户是否已登录(user.is_authenticated())
        #3.拿url_name到permission_dict去匹配,匹配时要包括请求方法和参数
        #4.拿匹配到可权限key,调用user.has_perm(key)
        match_results = [None,]
        request = args[0]
        resolve_url_obj = resolve(request.path)
        # 通过resolve解析出当前访问的url_name
        current_url_name = resolve_url_obj.url_name
        print('---perm:',request.user,request.user.is_authenticated(),current_url_name)
        #match_flag = False
        match_key = None
        #判断用户是否登录
        if request.user.is_authenticated() is False:
             return redirect(settings.LOGIN_URL)
    
        for permission_key,permission_val in  perm_dic.items():
            #key和value(值有四个参数): 比如 'crm_table_index': ['table_index', 'GET', [], {}, ]
            per_url_name = permission_val[0]
            per_method  = permission_val[1]
            perm_args = permission_val[2]
            perm_kwargs = permission_val[3]
    
            #如果当前访问的url_name匹配上了权限里面定义的url_name
            if per_url_name == current_url_name:
                #url_name匹配上,接着匹配方法(post,get....)
                if per_method == request.method:
                    # if not  perm_args: #if no args defined in perm dic, then set this request to passed perm
    
                    #逐个匹配参数,看每个参数是否都能对应的上。
                    args_matched = False      #for args only
                    for item in perm_args:
                        #通过反射获取到request.xxx函数   这里request_methon_func = request.GET/request.POST
                        request_method_func = getattr(request,per_method)
    
                        if request_method_func.get(item,None):   # request字典中有此参数
                            args_matched = True
                        else:
                            print("arg not match......")
                            args_matched = False
                            break          # 有一个参数不能匹配成功,则判定为假,退出该循环。因为可能有很多参数,必须所有参数都一样才匹配成功
                    else:         # perm_dic里面的参数可能定义的就是空的,就走这里
                        args_matched = True
    
                    #匹配有特定值的参数
                    kwargs_matched = False
                    for k,v in perm_kwargs.items():
                        request_method_func = getattr(request, per_method)
                        arg_val = request_method_func.get(k, None)  # request字典中有此参数
                        print("perm kwargs check:",arg_val,type(arg_val),v,type(v))
                        if arg_val == str(v): #匹配上了特定的参数 及对应的 参数值, 比如,需要request 对象里必须有一个叫 user_id=3的参数
                            kwargs_matched = True
                        else:
                            kwargs_matched = False
                            break # 有一个参数不能匹配成功,则判定为假,退出该循环。
    
                    else:
                        kwargs_matched = True
    
    
                    match_results = [args_matched,kwargs_matched]
                    print("--->match_results ", match_results)
                    #列表里面的元素都为真
                    if all(match_results): #都匹配上了
                        match_key = permission_key
                        break
    
        if all(match_results):
            # 主要是获取到app_name,match_key = 'app_staff_detail'
            app_name, *per_name = match_key.split('_')		# app_name: app, per_name=['staff', 'detail']
            print("--->matched ", match_results, match_key)
            print(app_name, *per_name)
            
            perm_obj = '%s.%s' % (app_name, match_key)		# per_obj = app.app_staff_detail
            print("perm str:", perm_obj)
            
            print('当前用户所有权限', request.user.get_all_permissions())       # {'app.app_staff_detail', 'app.app_fina_state'}
            
            # 使用 Permission 的 has_permission() 方法检查是否有权限,更多权限可在 auth_permission 表中查看
            if request.user.has_perm(perm_obj):
                print('当前用户有此权限')
                return True
            else:
                print('当前用户没有该权限')
                return False
    
        else:
            print("未匹配到权限项,当前用户无权限")
    
    
    def check_permission(func):
        def inner(*args,**kwargs):
            if not perm_check(*args,**kwargs):
                request = args[0]
                return render(request,'kingadmin/page_403.html')
            return func(*args,**kwargs)
        return  inner
    

    权限钩子函数:

    def view_my_own_customers(request):
        print("执行权限钩子函数.....")
        if str(request.user.id) == request.GET.get('consultant'):
            print("访问自己创建的用户,允许")
            return True
        else:
            return False
    

    使用:

    @login_required
    @check_permission
    def staff_detail(request):
        """员工个人信息"""
        user_obj = models.UserProfile.objects.all()
    
        return render(request, 'staff_detail.html', {'user_obj': user_obj})
    

    lila 没有查看员工信息权限,因此访问是 403:

    hj 是老板,所有页面都能查看,有权限:

    具体权限开关可在 admin 中配置:

    源码:https://github.com/hj1933/permission

  • 相关阅读:
    创建分区表(按照年份分区,自动新增分区)
    flash rock me
    苹果有虫才好吃
    Evolutility改造支持oracle
    Nhibernate问题三则
    Html5+razor+jqmobile尝鲜
    配置Instantclient
    T4,Redmine,Nhibernate etc
    monotouch开发ios应用手记
    大文件及文件夹上传(续)
  • 原文地址:https://www.cnblogs.com/midworld/p/11076074.html
Copyright © 2011-2022 走看看