zoukankan      html  css  js  c++  java
  • 基于中间件的RBAC权限控制

    RBAC 是什么

    RBAC(Role-Based Access Control,基于角色的访问控制),就是用户通过角色与权限进行关联。

    在 Django 中,权限就是用户对一个包含正则表达式 url 有没有访问的权利。

    RBAC 的实现

    在编写程序过程中,要注重弱耦合,所以在创建项目之初,应当将 RBAC 功能和逻辑功能分开来,创建不同的应用。

    基于 RBAC 的权限管理最根本上需要对数据表进行更改。实现该方法需要五张表,用户表,角色表,权限表,用户与角色关系表,角色与权限关系表。

    关于数据表的代码如下。

    class User(models.Model):
    	name=models.CharField(max_length=32)
    	pwd=models.CharField(max_length=32)
    	roles=models.ManyToManyField(to="Role")
    
    	def __str__(self): return self.name
    
    class Role(models.Model):
    	title=models.CharField(max_length=32)
    	permissions=models.ManyToManyField(to="Permission")
    
    	def __str__(self): return self.title
    
    class Permission(models.Model):
    	title=models.CharField(max_length=32)
    	url=models.CharField(max_length=32)
    
    	def __str__(self):return self.title
    

    登录校验

    对用户进行校验的前提是用户登录,并且用户登录信息浏览器要在其他页面当中依然能够取得,所以将用户 ID 存入 session 中。

    在用户登录时除了存储用户 ID 信息外,还应存储用户的所有权限信息。

    登录视图函数:

    from django.shortcuts import render, HttpResponse
    from rbac import models
    import re
    from rbac.service.permission import *
    
    
    # Create your views here.
    def login(request):
        if request.method == "POST":
            username = request.POST["username"]
            pwd = request.POST["pwd"]
            user_obj = models.User.objects.filter(name=username, pwd=pwd).first()
    
            # 登录用户存在
            if user_obj:
    
                # 将用户ID存入session
                request.session["user_id"] = user_obj.pk
    
                # 查询当前登录用户的所有权限,注册到session中
                initial_session(user_obj, request)
    
                return HttpResponse("登录成功!")
    
        return render(request, "login.html")
    
    

    查询并注册用户权限信息到 session 的代码如下:

    # /rbac/service/permission.py
    def initial_session(user, request):
        # 将用户权限列表存入session
        # 取出登录用户的权限url并去重
        # permissions = models.Role.objects.filter(user=user_obj).values("permissions__url").distinct()
        permissions = user.roles.all().values("permissions__url").distinct()
        permission_list = []
        for item in permissions:
            permission_list.append(item["permissions__url"])
    
        request.session["permission_list"] = permission_list
    

    在 session 中存储了用户 ID 信息后判断是否登录就很方便了:

    # 校验是否登录
    if not request.session.get("user_id"):
    	return redirect('/login/')
    

    校验权限

    前文中已经提到过,权限就是带正则表达式的 url ,对权限进行校验就是看当前 url 路径是否在用户权限列表中,如果在则用户有访问该 url 的权限。

    其中,在判断当前路径是否在权限列表中时应该使用正则匹配并注意要完全匹配,不能简单的使用
    元素 in 列表 这种简单判断。

    # 当前路径
    current_path = request.path_info
    
    # 校验权限
    flag = False
    permission_list = request.session.get("permission_list", [])
    for permission in permission_list:
    
    	# 保障url完全匹配
    	permission = "^%s$" % permission
    
    	ret = re.match(permission, current_path)
    	if ret:
    		flag = True
    		break
    
    if not flag:
    	return HttpResponse("没有权限访问")
    

    加入中间件

    有了实现权限校验的代码之后,如果想要在各个 url 中都进行校验就需要在每个相应的视图函数中填上相应的代码,造成了代码冗余。

    既然每个视图函数都需要进行校验,最好的办法就是使用中间件,项目启动后每次访问 url 都会自动走该中间件,从而进行了权限校验。

    有关中间件的知识请参考:Django 中的中间件

    使用中间件会发生一个问题:有些不需要校验的 url ,比如登录链接,注册链接,后台管理链接也都会进行校验从而报错,因此,需要对 url 设置一个白名单。

    # 校验是否为白名单
    valid_url_list = ["/login/", "/reg/", "/admin/.*"]
    for item in valid_url_list:
    	ret = re.match(item, current_path)
    	if ret:
    		return None
    

    中间件的完整代码:

    # /rbac/service/rbac.py
    
    from django.utils.deprecation import MiddlewareMixin
    import re
    from django.shortcuts import HttpResponse, redirect
    
    
    class ValidPermissionMiddleware(MiddlewareMixin):
    
        def process_request(self, request):
    
            # 当前路径
            current_path = request.path_info
    
            # 校验是否为白名单
            valid_url_list = ["/login/", "/reg/", "/admin/.*"]
            for item in valid_url_list:
                ret = re.match(item, current_path)
                if ret:
                    return None
    
            # 校验是否登录
            if not request.session.get("user_id"):
                return redirect('/login/')
    
            # 校验权限
            flag = False
            permission_list = request.session.get("permission_list", [])
            for permission in permission_list:
    
                # 保障url完全匹配
                permission = "^%s$" % permission
    
                ret = re.match(permission, current_path)
                if ret:
                    flag = True
                    break
    
            if not flag:
                return HttpResponse("没有权限访问")
    
            return None
    
    

    GitHub 地址:https://github.com/protea-ban/oldboy/tree/master/9day82/rbacDemo

  • 相关阅读:
    Bootstrap 模态对话框只加载一次 remote 数据的解决办法
    通过反射查找泛型的属性值
    基于Bootstrap的超酷jQuery开关按钮插件
    解決BufferedReader读取UTF-8文件中文乱码(转)
    Hibernate学习笔记
    freemarker XMLGregorianCalendar 转日期
    Android中手机号、车牌号正则表达式
    Eclipse中启动tomcat报错java.lang.OutOfMemoryError: PermGen space的解决方法
    WPF 引用DLL纯图像资源包类库中的图片
    “ sgen.exe ”已退出,代码为 1
  • 原文地址:https://www.cnblogs.com/banshaohuan/p/9858442.html
Copyright © 2011-2022 走看看