Flask实战2-用户角色

2017-09-27  本文已影响143人  猴子精h

本示例中有三种权限角色:匿名,普通用户,管理员,内容管理员(权限介于用户和管理员之间,可以修改上传文章的内容)

定于roles模型

class Role(db.Model):
    __tablename__ = 'roles'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(64), unique=True, index=True)
    # 只有一个角色需要设置为true,用户注册时,其角色会被设置成默认
    default = db.Column(db.Boolean, default=False, index=True)
    permissions = db.Column(db.Integer)
    user = db.relationship('User', backref='role', lazy='dynamic')

permission类型为整型,表示位标志。各种操作都对应一位,程序权限:

操作 位值 说明
关注用户 0b00000001 (0x01)
发表评论 0b00000010 (0x02)
写文章 0b00000100 (0x04)
管理评论 0b000001000 (0x08)
管理员权限 0b10000000 (0x80)

为方便使用,在模型中常量保存:

class Permission:
    FOLLOW = 0x01
    COMMENT = 0x02
    WRITE_ARTICLES = 0x04
    MODERATE_COMMENTS = 0x08
    ADMINISTER = 0x80

根据上面的权限位,可有得出各个角色使用的权限位,在验证权限时则使用与运算即可;

角色名 权限 说明
匿名 0b00000001 (0x00) 未登入,仅有阅读权限
用户 0b00000111 (0x07) 具有发布文章,发表评论和关注其他用户,默认角色
内容管理员 0b00001111 (0x0f)
管理员 0b11111111 (0xff)

在角色类中添加一个快速新建角色的方法。注意:'匿名'角色不需要在数据库中表示,这个角色的作用就是为了表示不在数据库中的用户。

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)
            print roles[r][0]
            role.permissions = roles[r][0]
            role.default = roles[r][1]
            db.session.add(role)
        db.session.commit()
    
# Use
python manage shell

Role.insert_roles()

角色验证

为了简化角色和权限的实现过程,我们可在User模型中添加一个辅助的方法,检查是否有制定的权限:

class User(UserMixin, db.Model):
    ...
    # 与运算验证权限
    def can(self, permissions):
        return self.role not None and (self.role.permissions & permissions == permissions)
        
    def is_administrator(self):
        return self.can(Permission.ADMINISTER)
        

# 出于一致性考虑,这里还定义了AnonymousUser类,继承Flask-Login的AnonymousUserMixin类
class AnonymousUser(AnonymousUserMixin):
    def can(self, permissions):
        return False

    def is_administator(self):
        return False

# 将其设为用户未登录时的current_user值, 这样程序不用先检查用户是否已经登入
login_manager.anonymous_user = AnonymousUser

检查用户权限的装饰器

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

def permission_required(permission):
    def decorator(f):
        @wraps(f)
        def decorated_function(*args, **kwargs):
            if not current_user.can(permission):
                abort(403)
            return f(*args, **kwargs)
        return decorated_function
    return decorator
    
def admin_required(f):
    return permission_required(Permission.ADMINISTER)(f)
    
# 使用
@main.route('/admin')
@login_required
@admin_required
def for_admin_only():
    return "For administrator"
    
@main.route('/moderator')
@login_required
@permission_required(Permission.MODERATE_COMMENTS)
def for_moderators_only():
    return "For comment moderators."

另外,为了模版中也可以全局使用Permission类,使用上下文管理器,能让变量在所有模版中全局访问

@main.app_context_processor
def inject_permission():
    return dict(Permission=Permission)
上一篇下一篇

猜你喜欢

热点阅读