Python Flask学习_用户角色和用户权限

在Web App中 ,存在和游客(匿名用户),普通用户,协管员,管理员等不同的角色,应当为不同的角色赋予不同的权限。各个用户只能在权限范围内访问页面、进行操作等。

通过一个例子来说明。本例中的用户分为4中:游客、普通用户、协管员、管理员。不同的用户有着不同的权限。

一、在数据库中表示不同的角色

权限有这么几种:

Python Flask学习_用户角色和用户权限_第1张图片

操作的权限使用8位二进制数表示,现在只用了5位,剩余的可以以后用来扩展。

权限的叠加是按位与运算,列如:拥有关注用户和写文章的权限应当是0b00000001&0b00000100,结果是0b00000101。

也容易反推出,0b00000011是0b00000001&0b00000010,因此权限是:关注用户和在他人文章中写评论。

用户的不同角色在数据库SQLite中存储,权限的位值(permission)作为角色的一个字段。

roles表的字段:

Python Flask学习_用户角色和用户权限_第2张图片

使用python代码实现:

# app/models.py

class Role(db.Model):
    __tablename__ = 'roles'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(64), unique=True)
    default = db.Column(db.Boolean,default=False,index=True)
    permissions = db.Column(db.Integer)                                      #值是整数,表示位标志。
    users = db.relationship('User', backref='role', lazy='dynamic')

在Role类中定义一个静态方法,提供将记录写入数据库的方法

# app/models.py

class Role(db.model):
    @staticmethod
    def insert_roles():
        '''并不直接创建角色,而是根据数据库现有角色,然后进行更新。以后角色有更改也可执行同样操作。'''
        roles = {
            'User':(Permission.FOLLOW |Permission.COMMENT |Permission.WRITE_ARTICLES ,True),
            'Moderator':(Permission.FOLLOW |Permission.COMMENT |Permission.WRITE_ARTICLES |Permission.MODERATE_COMMENTS,False),
            'Administrator':(0xff,False)
        }
        for r in roles:
            role = Role.query.filter_by(name=r).first()
            if role is None:
                role = Role(name=r)
            role.permissions = roles[r][0]
            role.default = roles[r][1]
            db.session.add(role)
        db.session.commit()

roles有三种:

'User':表示普通用户,权限值(permission)是(Permission.FOLLOW |Permission.COMMENT |Permission.WRITE_ARTICLES ,True),其中Permission是定义了各种权限的类(相当于C++中的枚举),default是True。

'Modetator':表示协管员,权限值也列出来了,default是False。

'Administrator':表示管理员,权限值是oxff,转化为二进制是0b11111111,表示拥有所有权限。default是False。

调用这个静态方法,会把三种角色和各自的权限值写入数据库中。

最后还有一个:游客,游客不需要登录,不需要在数据库中有表示。

以上完成后,可以使用shell对话,去写入数据库。

Python Flask学习_用户角色和用户权限_第3张图片

二、赋予角色

一个新用户注册以后,要给他赋予一个角色。看他是普通用户,还是协管员,还是管理员。、

新用户注册时,会实例化一个User类 ,然后调用db.session.add(u)和db.session.commit()直至提交到数据库。

在实例化User类时,根据邮箱判断角色。

# app/models

class User(UserMixin,db.Model):
    __tablename__ = 'users'
    id = db.Column(db.Integer, primary_key=True)                        #id列
    username = db.Column(db.String(64), unique=True, index=True)        #username列
    role_id = db.Column(db.Integer, db.ForeignKey('roles.id'))          #role_id列
    password_hash = db.Column(db.String(128))                            #密码hash列
    email = db.Column(db.String(64),unique=True,index=True)             #email列
    confirmed = db.Column(db.Boolean,default=False)

    def __init__(self,**kwargs):
        '''构造函数:首先调用基类构造函数,如果创建基类对象后没定义角色,则根据email地址决定其角色'''
        super(User,self).__init__(**kwargs)
        if self.role is None:
            if self.email == current_app.config['FLASKY_ADMIN']:
                self.role = Role.query.filter_by(permissions=0xff).first()
            if self.role is None:
                self.role = Role.query.filter_by(default=True).first()

role_id = db.Column(db.Integer, db.ForeignKey('roles.id'))          #users表

users = db.relationship('User', backref='role', lazy='dynamic')      #roles表
   role_id是外键,和roles.id联结。可以通过u.role访问到联结的role对象。

self.email == current_app.config['FLASKY_ADMIN']  :   判断email为特定地址的话,就设定 self.role = Role.query.filter_by(permissions=0xff).first()。

三、验证角色

为了更好更快的验证当前角色有没有某些权限,就为User定义了一些方法。

# app/models.py

class User(UserMixin,db.models):
    def can(self,permissions):
        '''检查permissions要求的权限角色是否允许'''
        return self.role is not None and (self.role.permissions & permissions)==permissions

    def is_administrator(self):
        '''检查是否管理员'''
        return self.can(Permission.ADMINISTER)

self.role is not None and (self.role.permissions & permissions)==permissions: 

要为True,需要满足两个条件:

1、role不为空,

2、role的权限值(self.role.permission)和要验证的权限(permission)的按位与操作值要和permission相等。

处于一致性考虑,还定义了AnonymousUserMixin类,并实现了can()方法和is_administrator()方法。再把它设为用户未登录时的current_user值。

# app/models.py

class AnonymousUser(AnonymousUserMixin):
    '''为了和User类保持一致,
    匿名登录时,使用current_user对象的类'''
    def can(self,permissions):
        return False

    def is_administrator(self):
        return False

login_manager.anonymous_user = AnonymousUser

用户分好了,不同用户的权限分好了,验证用户权限的方法也实现了。下面就是在视图函数中根据用户的不同 决定是否可以访问某些页面 。

首先,为了方便,定义了一些装饰器。

# app/decorators.py
'''定义装饰器'''

from functools import wraps
from flask import abort
from flask_login import current_user
from .models import Permission

def permission_required(permission):
    '''定义装饰器@permission_required(permission)'''
    def decorator(f):
        @wraps(f)
        def decorated_function(*args,**kwargs):
            if not current_user.can(permission):                        #如果当前用户不具有permission则抛出403错误。
                abort(403)
            return f(*args,**kwargs)
        return decorated_function
    return decorator

def admin_required(f):
    '''定义装饰器@admin_required'''
    return permission_required(Permission.ADMINISTER)(f)

注意:403.html也要实现一下。

然后,在视图函数中使用这些装饰器 

# app/main/views.py

@main.route('/admin')
@login_required
@admin_required
def for_admin_only():
    '''测试:管理员权限'''
    role = 'admin'
    return render_template('for_permission_test.html',role=role)

@main.route('/moderator')
@login_required
@permission_required(Permission.MODERATE_COMMENTS)
def for_moderator_only():
    '''测试:协管员权限'''
    role = 'moderator'
    return render_template('for_permission_test.html',role=role)

/admin的URL只有拥有管理员权限的用户才能访问。

/moderator的URL也只有拥有协管(以上)权限的用户才能访问。


在模板中使用Permission类的方法。

还记得Permission类是定义了各种权限的值的吗?为了在模板中也能使用Permission类,可以使用上下文管理器

# app/main/__init__.py

@main.app_context_processor
def inject_permissions():
    '''使用上下文处理器,是变量在模板全局中可访问'''
    return dict(Permission=Permission)










你可能感兴趣的:(python,flask学习)