Web 程序中的用户并非都具有同样地位。在大多数程序中,一小部分可信用户具有额外权
限,用于保证程序平稳运行。管理员就是最好的例子,但有时也需要介于管理员和普通用
户之间的角色,例如内容协管员。
有多种方法可用于在程序中实现角色。具体采用何种实现方法取决于所需角色的数量和细
分程度。例如:
1)简单的程序可能只需要两个角色,一个表示普通用户,一个表示管理员。
对于这种情况,在 User 模型中添加一个 is_administrator 布尔值字段就足够了。
2)复杂的程序可能需要在普通用户和管理员之间再细分出多个不同等级的角色。
有些程序甚至不能使用分立的角色,这时赋予用户某些权限的组合或许更合适。
本章介绍的用户角色实现方式结合了分立的角色和权限,赋予用户分立的角色,但角色使
用权限定义。
9.1 角色在数据库中的表示
第 5 章创建了一个简单的 roles 表,用来演示一对多关系。示例 9-1 是改进后的 Role 模型。
示例 9-1 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) # 设置unique唯一 default = db.Column(db.Boolean, default=False, index=True) # index为改字段创建索引,default为设置默认值 permissions = db.Column(db.Integer) # 以数字表示权限等级 users = db.relationship('User', backref='role', lazy='dynamic') # 动态关联
只有一个角色的 default 字段要设为 True,其他都设为 False。用户注册时,其角色会被
设为默认角色。
这个模型的第二处改动是添加了 permissions 字段,其值是一个整数,表示位标志。各操
作都对应一个位位置,能执行某项操作的角色,其位会被设为 1。
显然,各操作所需的程序权限是不一样的。对 Flasky 开说,各种操作如表 9-1 所示。
表9-1 程序的权限
操 作 位 值 说 明
关注用户 0b00000001(0x01) 关注其他用户
在他人的文章中发表评论 0b00000010(0x02) 在他人撰写的文章中发布评论
写文章 0b00000100(0x04) 写原创文章
管理他人发表的评论 0b00001000(0x08) 查处他人发表的不当评论
管理员权限 0b10000000(0x80) 管理网站
注意,操作的权限使用 8 位表示,现在只用了其中 5 位,其他 3 位可用于将来的扩充。
9-1 中的权限可使用示例 9-2 中的代码表示。
示例 9-2 app/models.py:权限常量
class Permission: FOLLOW = 0x01 COMMENT = 0x02 WRITE_ARTICLES = 0x04 MODERATE_COMMENTS = 0x08 ADMINISTER = 0x80
表 9-2 列出了要支持的用户角色以及定义角色使用的权限位。
表9-2 用户角色
用户角色 权 限 说 明
匿名 0b00000000(0x00)未登录的用户。在程序中只有阅读权限
用户 0b00000111(0x07) 具有发布文章、发表评论和关注其他用户的权限。这是新用户的默认角色
协管员 0b00001111(0x0f) 增加审查不当评论的权限
管理员0b11111111(0xff) 具有所有权限,包括修改其他用户所属角色的权限
(译注:有点乱,应该结合程序权限表9-1和9-2理解:二进制bit值共八位<0b后面8个零>默认无权限用户是8位全部0,操作分别有5种(关注,评论,撰写,屏蔽,管理权) 从右往左,依次设置为1,管理权使第八位。表9-2则是合并权限组合定义了角色,无权用户8位全是零,普通用户拥有撰写,评论,关注三项许可权限(权限标志位分别是1,2,3),所以二进制许可代码组合就是0b00000111,其他同理
)
示例 9-3 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()
insert_roles() 函数并不直接创建新角色对象,而是通过角色名查找现有的角色,然后再
进行更新。只有当数据库中没有某个角色名时才会创建新角色对象。如此一来,如果以后
更新了角色列表,就可以执行更新操作了。要想添加新角色,或者修改角色的权限,修改
roles 数组,再运行函数即可。注意,“匿名”角色不需要在数据库中表示出来,这个角色
的作用就是为了表示不在数据库中的用户。
若想把角色写入数据库,可使用 shell 会话:
(venv) $ python manage.py shell >>> Role.insert_roles() >>> Role.query.all() [<Role u'Administrator'>, <Role u'User'>, <Role u'Moderator'>]
>>>>未完待续。。。