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)