Django之权限(起步)

一. 权限概述

1. 认识权限

为什么要有权限? 因为权限让不同的用户拥有不同的功能. 权限可以对功能进行划分.

生活中处处有权限. 比如, 腾讯视频会员才有观看某个最新电影的权限, 你有房间钥匙就有了进入这个房间的权限. 同样, 程序开发过程中也有权限, 我们今天所说的权限指的是web网站权限, 对于不同用户访问web服务时应该有不同的功能. 例如: 一个公司有CEO, 销售主管, 销售等等, 不同的用户能访问的服务也不是完全相同的. 处于这样的需求下, 我们就需要权限控制了.

2. 为什么要开发权限组件?

组件可以减少代码的重复, 能提高我们的开发效率--开发一次组件, 那么在以后的项目中可以一直使用.

3. web开发中权限是指什么?

首先我们要认识到, web程序是通过 url 的切换来查看不同的页面(功能)的, 所以权限指的其实就是一个含有正则表达式的URL, 对url控制就是对权限的控制.

结论: 一个人有多少权限就取决于他有多少个URL的访问权限.

二. 权限表结构设计

1.版本一

按照生活中的实际情况来看, 一个用户有多个权限, 一个权限也可以分配给多个用户, 所以我们可以设计出下面的三张表:

分析: 设计完该表结构之后, 我们又考虑到这种情况: 如果我们再新增10个用户, 每个用户都有权限表中的2个权限, 这意味着我们就要在"权限和用户关联表"中新增40条记录. 如果再拓展到我们的实际生活中, 又会出现怎样的情况呢? 生活中一个web网站肯定不止2个url, 这就意味着每新增一个客户, 我们就要在某些表中增加若干条记录. 这样做不仅加重的程序员工作任务, 而且造成了数据库空间的严重浪费. 很显然, 此表有待改进!

2. 版本二

观察生活中的实际情况, 我们发现, 某些网站有普通用户和VIP用户, 部分网站的VIP用户也分了等级. 此时我们思考: 这样的做法实际上是给用户分了类, 把我们的用户分为了不同的角色. 是的, 此时我们再进一步联想: 我们是否也可以对用户进行角色划分, 然后再对不同的角色进行权限的分配呢?

  • 分析:
    • 一个人可以有多个角色
    • 一个角色可以有多个人
    • 一个角色可以有多个权限
    • 一个权限可以分配给多个角色

以下是我们的表结构设计:

调整以后的表结构, 由原来的基于用户的权限控制转换成基于角色的权限控制(RBAC), 以后再进行权限分配时只需要给指定角色分配一次权限, 给众多用户分配指定角色即可.

三. Django项目中进行权限设置

步骤概述:

  • 1.数据库配置(使用Django自带数据库即可)
  • 2.models.py文件: 创建三张表, 分别是用户表, 角色表, 权限表
  • 3.使用Django自带的admin来对各个表进行数据添加
  • 4.路由匹配(urls.py文件)
  • 5.视图函数(views.py文件)
  • 6.自定义中间件

Django生命周期的回顾

在进行Django权限设置之前, 我们首先要回顾一下Django的生命周期:

1. models.py文件配置

  • 创建项目MyLuffyPermission, app名称为rbac.
  • rbac/models.py: 我们进行权限表结构设计时, 共有5张表, 其中2张是关系表, 所以models.py文件中只有3个model, 分别是权限表, 角色表和用户表.
  • 数据库迁移指令
python manage.py makemigrations
python manage.py migrate

2. 使用Django自带的admin对表进行数据添加

(1) 创建超级用户

(2) 对rabc/admin.py文件进行配置

from django.contrib import admin
from rbac import models


class PermissionModelAdmin(admin.ModelAdmin):
    list_display = ['title', 'url']  # 展示的字段
    list_editable = ['url']  # 编辑的字段


admin.site.register(models.Permission, PermissionModelAdmin)
admin.site.register(models.Role)
admin.site.register(models.User)

(3)启动Django项目, 浏览器输入127.0.0.1:8000/admin/, 回车. 对数据库进行相关信息的添加与修改.

3. rbac组件的开发

在rabc应用中创建一个middlewares的文件夹, 在该文件夹中新建rabc.py, 它就是我们自定义的一个中间件, 作用是权限校验.

  • rabc/middlewares/rabc.py: 该中间件的编写分三步走:
    • (1)获取当前访问的url
    • (2)获取当前用户权限信息
    • (3)权限校验
  • 具体代码如下所示:
from django.utils.deprecation import MiddlewareMixin
from django.conf import settings
from django.shortcuts import HttpResponse
import re


class RabcMiddleware(MiddlewareMixin):

    def process_request(self, request):
        # 1.获取当前访问的URL
        url = request.path_info
        print(111111)
        # 如果当前的url是白名单中的内容,return None,即允许该url通过校验
        for i in settings.WHITE_LIST:
            if re.match(i, url):  # 如果匹配成功
                return  # 通过校验

        # 2.获取当前用户的权限信息  注意session中的键值对是在登录的时候(web/views/account)设置的
        permission_list = request.session.get(settings.PERMISSION_SESSION_KEY)
        print(222222)
        # 3.权限的校验
        for i in permission_list:
            # URL权限 匹配成功
            if re.match(r"^{}$".format(i['permissions__url']), url):
                print(333333)
                return
        # 校验失败,拒绝访问
        return HttpResponse('没有访问权限')

4. 路由匹配, 视图函数

(1) MyluffyPermission/urls.py

(2) 创建一个名叫web的app应用, 并在该应用中创建urls.py文件

# 创建app
python manage.py startapp app的名称

# 注册app
找到MyluffyPermission/settings.py文件下的INSTALLED_APPS项,然后注册app

(3) 在web/urls.py文件中写我们的路由匹配

(4) 在web应用下创建一个名为viewspython包, 对视图函数进行分类, 每个角色的功能单独用一个.py文件来写.

5. 登录功能

在成功配置完我们的rbac组件之后(主要包括rbac/middlewares/rbac.pyrbac/migrations/middles.py), 我们现在开始进行对登录功能的编写.

(1)路由匹配: web/urls.py

from django.conf.urls import url
from web.views import account

urlpatterns = [
	# 用户登录
    url(r'^login/$', account.login, name='login'),
]

(2)视图函数: web/views/account.py

from django.shortcuts import HttpResponse, redirect, render, reverse
from rbac import models
from django.conf import settings


def login(request):
    if request.method == 'POST':
        # 获取提交的数据
        user = request.POST.get('user')
        pwd = request.POST.get('pwd')
        # 数据库匹配管理对象
        obj = models.User.objects.filter(name=user, password=pwd).first()
        if not obj:  # 错误优先
            return render(request, 'login.html', {'error_msg': '用户名或密码错误'})
        # 登录成功,保存用户权限信息:
        ret = obj.roles.all().filter(permissions__url__isnull=False).values(
            'permissions__url',
			'permissions__title',).distinct()
        request.session[settings.PERMISSION_SESSION_KEY] = list(ret)
        
        return HttpResponse("登录成功")
    return render(request, 'login.html')

account.py文件中其中两行代码的解释:

ret = obj.roles.all().filter(permissions__url__isnull=False).values(
   	'permissions__url',
    'permissions__title',).distinct()

"""
obj -- User对象

.roles.all() -- 通过User对象拿到该用户的所有角色对象(roles)

.filter(permissions__url__isnull=False)
	-- permissions__url ==> 角色对象(roles)跨表查询Permission表的 URL权限(url)信息
	-- isnull=False ==> isnull表示空,那么isnull=False表示不为空,也就是说 整个ORM语句筛选出了"URL权限不为空的角色对象"
	
.values('permissions__url','permissions__title')
	-- 拿到角色的 URL权限和权限标题 组成的字典
	
.distinct() -- 去重
"""
request.session[settings.PERMISSION_SESSION_KEY] = list(ret)

# request.session[key] = value表示设置session数据
# 由ret=xxx.values(xxx)可知:ret是一个QuerySet对象.
# 由于request.session设置session时,会把数据转换为json格式,而ret却是一个QuerySet,因此我们把ret转换为列表后,就可以设置session了

# PERMISSION_SESSION_KEY是我们自定义的一个配置信息,我们把它写在settings.py文件里. 配置它的目的是:今后我们再次设置session时,它的key就是一个动态的可变的,如果需要修改key,我们只需要在settings.py文件里重新配置即可,而不再需要找到对应的函数,重复修改.从而达到简化我们代码修改的工作量.

(3)模板文件: web/templates/login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<form action="" method="post">
    {% csrf_token %}
    <p>
        用户名:<input type="text" name="user">
    </p>
    <p>
        密码:<input type="password" name="pwd">
    </p>
    <p style="color: red">{{ error_msg }}</p>
    <button>登录</button>
</form>

</body>
</html>
posted @ 2018-12-25 19:41  咕噜噜~  阅读(2600)  评论(0编辑  收藏  举报