基於Django中間件的權限認證組件

用戶權限認證組件包括權限model類和中間件類python

model類介紹

Permission

權限類git

權限基本信息包括title:權限名稱 url:權限具體urlgithub

Role

角色類數據庫

角色類包括title:角色名稱 permission:角色的權限django

權限和角色多對多關係session

用戶類須要對Role類設置多對多映射app

Whitelist

白名單類函數

白名單包括title:權限名稱 url:權限具體urlurl

白名單內存放的是系統放行不進行權限校驗的urlspa

NeedLogin

登陸驗證包括title:url標題 url:具體url

登陸驗證表存放的是須要登陸才能進行操做或權限校驗的url,若是沒有登陸則會重定向到登陸頁面

models.py

from django.db import models


class Permission(models.Model):
    '''
    權限基本信息包括title:權限名稱 url:權限具體url
    '''
    title = models.CharField(max_length=32, verbose_name='權限名稱')
    url = models.CharField(max_length=200, verbose_name='url')

    def __str__(self):
        return self.title

    class Meta:
        verbose_name = '權限'
        verbose_name_plural = verbose_name


class Role(models.Model):
    '''
    角色類包括title:角色名稱 permission:角色的權限
    權限和角色多對多關係
    用戶類須要對Role類設置多對多映射
    '''
    title = models.CharField(max_length=32, verbose_name='角色名稱')
    permission = models.ManyToManyField(Permission, verbose_name='權限')

    def __str__(self):
        return self.title

    class Meta:
        verbose_name = '角色'
        verbose_name_plural = verbose_name


class Whitelist(models.Model):
    '''
    白名單包括title:權限名稱 url:權限具體url
    白名單內存放的是系統放行不進行權限校驗的url
    '''
    title = models.CharField(max_length=32, verbose_name='權限名稱')
    url = models.CharField(max_length=200, verbose_name='url')

    def __str__(self):
        return self.title

    class Meta:
        verbose_name = '白名單'
        verbose_name_plural = verbose_name


class NeedLogin(models.Model):
    '''
    登陸驗證包括title:url標題 url:具體url
    登陸驗證表存放的是須要登陸才能進行操做或權限校驗的url,若是沒有登陸則會重定向到登陸頁面
    '''
    title = models.CharField(max_length=32, verbose_name='url標題')
    url = models.CharField(max_length=200, verbose_name='url')

    def __str__(self):
        return self.title

    class Meta:
        verbose_name = '登陸驗證表'
        verbose_name_plural = verbose_name

中間件類介紹

VerifyPermissions

權限認證中間件實現了process_request和process_view方法。中間件在初始化時會從數據庫更新白名單列表和須要登陸認證的url列表,這個中間件實現了用戶訪問權限認證,登陸認證功能。

verify.py

from django.shortcuts import render, HttpResponse, redirect
from permission.models import Whitelist, NeedLogin
from django.utils.deprecation import MiddlewareMixin
import re
from django.conf import settings


class VerifyPermissions(MiddlewareMixin):
    # 只在中間件加載時執行一次
    # 獲取白名單列表
    whitelist = Whitelist.objects.values('url').all()
    # 獲取須要登陸的url列表
    needLogin_list = NeedLogin.objects.values('url').all()

    def process_request(self, request):
        '''
        對用戶的請求和登陸驗證的url進行匹配,若是匹配成功且用戶沒有登陸,則重定向到登陸頁面
        須要在settings.py配置LOGIN_URL
        :param request:
        :return:
        '''
        current_url = request.path
        # 從session中取用戶登陸的數據,在登陸視圖函數內,登陸成功須要在session中存儲登陸用戶信息,這裏使用user做爲登陸用戶信息的key
        user = request.session.get("user", "")
        # 校驗是不是登陸驗證表的內容,若是是且已登陸則放行,若是不是登陸驗證表的內容則會放行到urls.py,若是未登陸則重定向到登陸頁面
        for url in self.needLogin_list:
            ret = re.fullmatch(url['url'], current_url)
            if ret:
                if user:
                    return
                else:
                    return redirect(settings.LOGIN_URL)

    def process_view(self, request, view_func, view_args, view_kwargs):
        '''
        根據中間中process_*方法執行的順序,process_view方法是在請求到達urls.py以後在執行view.py視圖函數以前執行的
        使用process_view方法不使用process_request是由於若是用戶輸入了找不到的路徑,能夠先提示404,不會先提示權限問題
        '''
        current_url = request.path
        # 校驗是不是白名單的內容,若是是則放行
        for url in self.whitelist:
            ret = re.fullmatch(url['url'], current_url)
            if ret:
                return
        # 從session中獲取權限列表
        permissions_list = request.session.get('permissions_list', [])
        # 校驗是不是權限內的內容,若是不是提示權限不夠
        for url in permissions_list:
            ret = re.fullmatch(url['url'], current_url)
            if ret:
                return
        else:
            # 可設置自定義頁面
            return HttpResponse('權限不夠')

管理員經過django的admin更新白名單或者登陸驗證表時須要及時更新中間件中的白名單列表和登陸驗證url列表,避免重啓項目。這裏使用重寫admin.ModelAdmin的save_model和delete_model方法。

admin.py

from django.contrib import admin
from permission.models import *
from permission.verify import VerifyPermissions


class WhitelistAdmin(admin.ModelAdmin):
    '''
    因爲只在中間件加載第一次時加載白名單,因此對白名單內容作修改時,應對中間件中內容更新
    '''

    def save_model(self, request, obj, form, change):
        obj.save()
        VerifyPermissions.whitelist = Whitelist.objects.values('url').all()

    def delete_model(self, request, obj):
        obj.delete()
        VerifyPermissions.whitelist = Whitelist.objects.values('url').all()


class NeedLoginAdmin(admin.ModelAdmin):
    '''
    因爲只在中間件加載第一次時加載登陸驗證表,因此對登陸驗證表內容作修改時,應對中間件中內容更新
    '''

    def save_model(self, request, obj, form, change):
        obj.save()
        VerifyPermissions.needLogin_list = NeedLogin.objects.values('url').all()

    def delete_model(self, request, obj):
        obj.delete()
        VerifyPermissions.needLogin_list = NeedLogin.objects.values('url').all()


admin.site.register(Permission)
admin.site.register(Role)
admin.site.register(Whitelist, WhitelistAdmin)
admin.site.register(NeedLogin, NeedLoginAdmin)

使用說明:

組件Github地址:https://github.com/WPN0837/RBAC

PS:Github上是組件的一個使用示例

只須要把permission下的內容複製到須要權限認證的項目中便可使用

在使用這個組件的項目中註冊這個組件,在settings.py的INSTALLED_APPS添加'permission.apps.PermissionConfig',例如:

INSTALLED_APPS = [
    '''
    'permission.apps.PermissionConfig',
]

添加中間件,在settings.py的MIDDLEWARE添加'permission.verify.VerifyPermissions',例如:

MIDDLEWARE = [
    '''
    'permission.verify.VerifyPermissions',
]

PS:VerifyPermissions必定要寫在SessionMiddleware後面,由於VerifyPermissions使用到了session

使用須要注意的地方:

在項目定義User類的地方須要先引入permission.models下的Role類

例如from permission.models import Role

再在User綁定User類與Role類多對多映射關係

具體如:

class UserInfo(models.Model):
    username = models.CharField(max_length=20)
    pwd = models.CharField(max_length=20)
    # 綁定用戶與角色多對多映射關係
    role = models.ManyToManyField(Role, verbose_name='角色')

    def __str__(self):
        return self.username

在配置文件settings.py文件中須要配置登陸的URL,要與urls.py文件中相同,例如:

# settings.py
# 也可以使用登陸url的別名
LOGIN_URL = "/login/"
# urls.py
urlpatterns = [
    path('admin/', admin.site.urls),
    path('', index),
    # 登陸的url,也可使用別名
    path('login/', login),
]

在用戶登陸視圖函數裏,若是登陸成功應當把用戶信息和用戶權限信息保存到用戶的session中,例如:

def login(request):
    if request.method == 'GET':
        name = request.GET.get('name', '')
        pwd = request.GET.get('pwd', '')
        u = UserInfo.objects.filter(username=name, pwd=pwd).first()
        if u:
            # 保存用戶信息
            request.session['user'] = name
            # 保存用戶權限信息
            request.session['permissions_list'] = list(
                Permission.objects.filter(role__in=u.role.all()).values('url').all())
            return HttpResponse('登陸成功')
        else:
            return HttpResponse('登陸失敗')

具體保存到session裏的數據的key由中間件類VerifyPermissions的process_request(使用了user)和process_view(使用了permissions_list)方法裏使用到session的地方決定,可本身修改。

在添加完組件後,首先遷移數據庫文件,而後把中間件VerifyPermissions註釋掉,再進入django admin管理後臺,在白名單中添加/admin/.*這個url,權限名稱能夠設置成admin或者其餘的,再取消中間件VerifyPermissions的註釋,由於沒有admin後臺的url添加進白名單,會提示沒有權限訪問/admin/開頭的url。

相關文章
相關標籤/搜索