• 基于RBAC权限验证, 中间价middleware实现, views 登录视图代码


    废话不多说  上代码:

    基础实现:

    rom django.shortcuts import HttpResponse, redirect, render
    from django.http import FileResponse, JsonResponse
    
    from rbac.models import *
    
    
    def login(request):
        if request.method == "POST":
            user = request.POST.get("user")
            pwd = request.POST.get("pwd")
    
            current_user = UserInfo.objects.filter(name=user, password=pwd).first()
     '''SELECT "rbac_userinfo"."id", "rbac_userinfo"."name", "rbac_userinfo"."password", "rbac_userinfo"."email" 
        FROM "rbac_userinfo" 
        WHERE ("rbac_userinfo"."name" = 'alex' AND "rbac_userinfo"."password" = '123') 
        ORDER BY "rbac_userinfo"."id" ASC  LIMIT 1; '''
            if not current_user:
                return render(request, "login.html", {"msg": "username or password is wrong"})
    
            # 根据当前用户信息,获取当前用户所拥有的所有的权限(queryset对象 是不能直接放入,session中的)
            permission_queryset = current_user.roles.filter(permissions__isnull=False).values("permissions__url").distinct()
    '''
    SELECT DISTINCT "rbac_permission"."url" FROM "rbac_role" 
        INNER JOIN "rbac_userinfo_roles" 
            ON ("rbac_role"."id" = "rbac_userinfo_roles"."role_id") 
        INNER JOIN "rbac_role_permissions" 
            ON ("rbac_role"."id" = "rbac_role_permissions"."role_id") 
        INNER JOIN "rbac_permission" 
            ON ("rbac_role_permissions"."permission_id" = "rbac_permission"."id") 
        WHERE ("rbac_userinfo_roles"."userinfo_id" = 1 AND "rbac_role_permissions"."permission_id" IS NOT NULL);
    '''
    
            # 根据得到的queryset 循环遍历, 得到其中的 每一个权限的 url. 然后将每一个url 放入一个列表中。进而保存到session
            permission_list = [item.get("permissions__url") for item in permission_queryset]
            print(permission_list)
            request.session["permissions__url"] = permission_list
            return redirect("/customer_list/")
        return render(request, "login.html", locals())
    login
    from django.utils.deprecation import MiddlewareMixin
    from django.shortcuts import HttpResponse, redirect
    import re
    
    class CheckPermission(MiddlewareMixin):
        '''用户权限信息的校验'''
        def process_request(self, request):
            '''当用户请求进入时 触发执行'''
            '''
            1. 获取当前用户请求的url
            2. 获取当前用户在session中保存的 权限列表 [......]
            3. 当前请求url 在 session中, 就可以,进行访问
            '''
    '''有时我们需要 一些url无需权限, 就可以登录。 比如login登录页面, amdin后台管理(这个不需要我们的中间件去控制权限,所以这里直接放过)'''
    '''我们尽量使用re 的正则匹配来做这件 事情。因为 我们不可能把 所有的url 都写一遍放到 白名单中。'''
    '''我这里使用的是 re.match() 他的作用是, 从一个字符串的起始位置 开始匹配一个模式。 如果第一个字符就不匹配 就直接返回None'''
    ''' re.match('/admin/',"/admin/")可以匹配成功, re.match('/admin/',"/admin/asdkd/asd")也是可以匹配成功的。
        所以已admin起始的url都属于白名单'''
    
         valid_url_list = ["/login/", "/admin/"]  # 一个白名单
            current_url = request.path_info  # 得到当前访问的url
            for valid_url in valid_url_list:
                if re.match(valid_url, current_url):  # 白名单中的url 无需权限验证
                    return None 
                # 我这里的return None 意思是: 如果在白名单中 我这里就直接return 函数中后续的代码就不会去执行,也就是不会再去
                # 进行验证的操作。  但是因为return 的是 None, 并不会影响整体的流程, 依然会继续执行后面的中间件,视图函数。
                # 如果 不是return None 的话。就不会走到视图函数中。
    
            permission_list = request.session.get("permissions__url")  # 得到保存在session中的 权限信息。
    
            if not permission_list:
                '''用户未登录前 session为空, 所以直接返回就好了'''
                return HttpResponse("您没有访问权限...请联系管理员")
    
            # permission_list 中的url 是包含正则的。 所以要使用re模块来进行验证
            flag = False
            # 需要注意的是,列表循环。进行匹配 只要有一个能匹配成功就可以访问, 所以未匹配成功 也不能够影响后面的匹配
            # 所以 就不能使用else。
            # 所以 添加一个flag变量 默认为False 只要有一个成功,flag就为Ture, 最终通过判断flag 确定是否拥有访问权限
            for url in permission_list:
                reg = "^%s$" % url  # 正则匹配match 都是从其实开始匹配,但是没有限定结束。
                # 所以要给当前的url  添加 ^ $ 开始和终止符。
                if re.match(reg, current_url):
                    flag = True
                    break  # 只要有一个匹配成功,就直接退出,剩下的就不需要进行匹配了
            if not flag:
                return HttpResponse("无权访问")
    middleware

    代码完善:

    上面的代码, 功能上是OK的, 但是不够profession。

     我们希望的是 一个,比较通用的组件。 而权限初始化这部分的代码,我们可以单独的提取出来,放入我们的rbac组件中。
    与我们的登录 进行一个 解耦。  这样 权限初始化这部分,独立出去之后,就不需要再重复的 编写:

    1.用户登录与权限初始化的 拆分:

    from django.shortcuts import HttpResponse, redirect, render
    from rbac.models import *
    from rbac.service.init_permission import init_permission
    
    
    def login(request):
        if request.method == "POST":
            user = request.POST.get("user")
            pwd = request.POST.get("pwd")
    
            current_user = UserInfo.objects.filter(name=user, password=pwd).first()
    
            if not current_user:
                return render(request, "login.html", {"msg": "username or password is wrong"})
    
            init_permission(current_user, request)
            return redirect("/customer/list/")
        return render(request, "login.html", locals())
    web.views.account
    def init_permission(current_user, request):
        '''
        :param current_user: 当前请求 用户对象
        :param request:  当前请求 数据
        :return:
        '''
        # 2. 权限 初始化
        permission_queryset = current_user.roles.filter(permissions__isnull=False).values("permissions__url").distinct()
        permission_list = [item.get("permissions__url") for item in permission_queryset]
        request.session["permissions__url"] = permission_list
    rbac.service.init_permission

    这样我们将,这部分的功能, 独立到我们的RBAC 权限组件中。 需要使用的时候, 直接调用即可。

    2. 我们已经完成了对 功能的拆分。 人的嫌麻烦程度是无限的。
           如果有一天,我想对 request.session["permissions__url"] = permission_list session的key 进行修改的时候,我就不得不去找到所有的 代码然后再去修改一遍。 这是非常不友好的,so We need a solution:
    思路:我们可以在 项目的settings中,添加一个 对这个 key 的 配置项:

            比如: PERMISSIONS_SESSION_KEY = "permissions__url"  

    from django.conf import settings
    
    def init_permission(current_user, request):
        '''
        :param current_user: 当前请求 用户对象
        :param request:  当前请求 数据
        :return:
        '''
        # 2. 权限 初始化
    
        permission_queryset = current_user.roles.filter(permissions__isnull=False).values("permissions__url").distinct()
        permission_list = [item.get("permissions__url") for item in permission_queryset]
        request.session[settings.PERMISSIONS_SESSION_KEY] = permission_list
    rbac.service.init_permission

    这样子, 我们以后保存的时候session的 时候, 就只需要引入settings中的 PERMISSIONS_SESSION_KEY 就ok了

    当然,在中间件需要取出 session的时候,也是同样的操作:

    web.md.rbac_middleware.CheckPermission
    class CheckPermission(MiddlewareMixin):
        '''用户权限信息的校验'''
        def process_request(self, request):
            '''当用户请求进入时 触发执行'''
            '''
            1. 获取当前用户请求的url
            2. 获取当前用户在session中保存的 权限列表 [......]
            3. 当前请求url 在 session中, 就可以,进行访问
            '''
    
            valid_url_list = ["/login/", "/admin/"]
            current_url = request.path_info
            for valid_url in valid_url_list:
                if re.match(valid_url, current_url):
                    return None
            permission_list = request.session.get(settings.PERMISSIONS_SESSION_KEY)
    
            if not permission_list:
                return HttpResponse("您没有访问权限...请联系管理员")
    
            flag = False
            for url in permission_list:
                reg = "^%s$" % url
                if re.match(reg, current_url):
                    flag = True
                    break 
            if not flag:
                return HttpResponse("无权访问")

     

  • 相关阅读:
    有了这个算法,图像上文字擦除再也用不上PS了
    说说Golang goroutine并发那些事儿
    Redis Sentinel 源码:Redis的高可用模型分析
    从架构设计理念到集群部署,全面认识KubeEdge
    如何极速极速搭建个人博客?Copy攻城狮用的这一招很优秀!
    Python进阶丨如何创建你的第一个Python元类?
    逼疯UE设计师,不可不知的提升产品用户体验的10个测试方法
    一文总结GaussDB通信原理知识
    目标检测推理部署:优化和部署
    GPU上的快速光谱图分区
  • 原文地址:https://www.cnblogs.com/chengege/p/10695845.html
Copyright © 2020-2023  润新知