Flask:用户角色与权限管理

Web 应用中的用户并非都具有同等地位,为此,要为所有用户分配一个角色

在应用中实现角色有多种方法。具体采用何种实现方法取决于所需角色的数量和细分程度。例如,简单的应用可能只需要两个角色,一个表示普通用户,一个表示管理员。对于这种情况,在 User 模型中添加一个 is_administrator 布尔值字段可能就够了。复杂的应用可能需要在普通用户和管理员之间再细分出多个不同等级的角色。有些用户可能又有多个角色,故赋予用户一系列独立的权限或许更合适。

以下介绍的用户角色实现方式结合了分立的角色和权限,赋予用户分立的角色,但是各个角色都通过权限列表定义允许用户执行的操作。


1、数据库中表示


1.1 定义角色表

class Role(db.Model):
    __tablename__ = 'roles'
    # ...
    permissions = db.Column(db.Integer, default=0)

permissions字段表示角色的权限,含义如下:

操作 权限名 权限值
关注用户 FOLLOW 1
在他人的文章中发表评论 COMMENT 2
写文章 WRITE 4
管理他人发表的评论 MODERATE 8
管理员权限 ADMIN 16

1.2 定义权限常量

class Permission:
    FOLLOW = 1
    COMMENT = 2
    WRITE = 4
    MODERATE = 8
    ADMIN = 16

使用 2 的幂表示权限值有个好处:每种不同的权限组合对应的值都是唯一的,方便存入角色的 permissions 字段。


1.3 管理角色权限

添加这些权限常量之后,可以在 Role 模型中定义几个新方法,用于管理权限:

class Role(db.Model):
    # ...

    def add_permission(self, perm):
        if not self.has_permission(perm):
            self.permissions += perm

    def remove_permission(self, perm):
        if self.has_permission(perm):
            self.permissions -= perm

    def reset_permissions(self):
        self.permissions = 0

    def has_permission(self, perm):
        return self.permissions & perm == perm

示例:

>>> role = Role(name='User')
>>> role.add_permission(Permission.WRITE)
>>> r.has_permission(Permission.WRITE)
True

可在模型中增加静态方法,用于生成常用角色:

class Role(db.Model):
	# ...

    @staticmethod
    def insert_roles():
        roles = {
            'User': [Permission.FOLLOW, Permission.COMMENT, Permission.WRITE],
            'Moderator': [Permission.FOLLOW, Permission.COMMENT,
            Permission.WRITE, Permission.MODERATE],
            'Administrator': [Permission.FOLLOW, Permission.COMMENT,
            Permission.WRITE, Permission.MODERATE,
            Permission.ADMIN],
        }
        
        for r in roles:
        # ...

2、给用户添加角色

  • 设置默认角色(生成User对象时自动配置角色信息)

    class User(UserMixin, db.Model):
        # ...
        def __init__(self, **kwargs):
            super(User, self).__init__(**kwargs)
            if self.role is None:
                if self.email == current_app.config['FLASKY_ADMIN']:
                    self.role = Role.query.filter_by(name='User').first()
                    # ...
    
  • 生成用户后手动设置角色

    user = User(name = 'xxx')
    
    user.role = Role.query.filter_by(name='User').first()
    

3、检测用户权限

为了简化角色和权限的实现过程,可在 User 模型中添加一个辅助方法,检查赋予用户的角色是否有某项权限。

from flask_login import UserMixin, AnonymousUserMixin

class User(UserMixin, db.Model):
    # ...

    def can(self, perm):
        return self.role is not None and self.role.has_permission(perm)

    def is_administrator(self):
        return self.can(Permission.ADMIN)

class AnonymousUser(AnonymousUserMixin):
    def can(self, permissions):
        return False

    def is_administrator(self):
        return False

login_manager.anonymous_user = AnonymousUser

如果想让视图函数只对具有特定权限的用户开放,可以使用自定义的装饰器。下面实现了两个装饰器,一个用于检查常规权限,另一个专门检查管理员权限。

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.ADMIN)(f)

使用示例:

@main.route('/admin')
@login_required
@admin_required
def for_admins_only():
    return "For administrators!"

4、在模板中使用权限常量

app/main/__init__.py:把 Permission 类加入模板上下文:

@main.app_context_processor
def inject_permissions():
    return dict(Permission=Permission)

5、测试

tests/test_user_model.py:添加角色和权限的单元测试

class UserModelTestCase(unittest.TestCase):
    # ...

    def test_user_role(self):
        u = User(email='john@example.com', password='cat')
        self.assertTrue(u.can(Permission.FOLLOW))
        self.assertTrue(u.can(Permission.COMMENT))
        self.assertTrue(u.can(Permission.WRITE))
        self.assertFalse(u.can(Permission.MODERATE))
        self.assertFalse(u.can(Permission.ADMIN))

    def test_anonymous_user(self):
        u = AnonymousUser()
        self.assertFalse(u.can(Permission.FOLLOW))
        self.assertFalse(u.can(Permission.COMMENT))
        self.assertFalse(u.can(Permission.WRITE))
        self.assertFalse(u.can(Permission.MODERATE))
        self.assertFalse(u.can(Permission.ADMIN))
posted @ 2021-03-27 14:38  yangblood  阅读(4448)  评论(0编辑  收藏  举报