Flask Vue.js全栈开发|第15章:权限管理
Synopsis: Web 应用中并不是每个用户的权限都一样,本章我们将实现简单的 RBAC(Role-based access control) 权限控制系统,创建多个角色,每个用户属于一个角色,每个角色所拥有的权限不同。比如只有管理员角色才能访问管理后台、编辑才能发布文章
本系列的最新代码将持续更新到: http://www.madmalls.com/blog/post/latest-code/
1. 角色 Role
每个角色可以拥有多个用户,所以 Role
与 User
是 一对多关系
,可以参考: http://www.madmalls.com/blog/post/post-curd-and-markdown/#12-one-to-many
1.1 定义模型
修改 back-end/app/models.py
,增加:
class Permission: '''权限认证中的各种操作,对应二进制的位,比如 FOLLOW: 0b00000001,转换为十六进制为 0x01 COMMENT: 0b00000010,转换为十六进制为 0x02 WRITE: 0b00000100,转换为十六进制为 0x04 ... ADMIN: 0b10000000,转换为十六进制为 0x80 中间还预留了第 4、5、6、7 共4位二进制位,以备后续增加操作权限 ''' # 关注其它用户的权限 FOLLOW = 0x01 # 发表评论、评论点赞与踩的权限 COMMENT = 0x02 # 撰写文章的权限 WRITE = 0x04 # 管理网站的权限(对应管理员角色) ADMIN = 0x80 class Role(PaginatedAPIMixin, db.Model): __tablename__ = 'roles' id = db.Column(db.Integer, primary_key=True) slug = db.Column(db.String(255), unique=True) name = db.Column(db.String(255)) # 角色名称 default = db.Column(db.Boolean, default=False, index=True) # 当新增用户时,是否将当前角色作为默认角色赋予新用户 permissions = db.Column(db.Integer) # 角色拥有的权限,各操作对应一个二进制位,能执行某项操作的角色,其位会被设为 1 users = db.relationship('User', backref='role', lazy='dynamic') def __init__(self, **kwargs): super(Role, self).__init__(**kwargs) if self.permissions is None: self.permissions = 0 @staticmethod def insert_roles(): '''应用部署时,应该主动执行此函数,添加以下角色 注意: 未登录的用户,可以浏览,但不能评论或点赞等 shutup: 0b0000 0000 (0x00) 用户被关小黑屋,收回所有权限 reader: 0b0000 0011 (0x03) 读者,可以关注别人、评论与点赞,但不能发表文章 author: 0b0000 0111 (0x07) 作者,可以关注别人、评论与点赞,发表文章 administrator: 0b1000 0111 (0x87) 超级管理员,拥有全部权限 以后如果要想添加新角色,或者修改角色的权限,修改 roles 数组,再运行函数即可 ''' roles = { 'shutup': ('小黑屋', ()), 'reader': ('读者', (Permission.FOLLOW, Permission.COMMENT)), 'author': ('作者', (Permission.FOLLOW, Permission.COMMENT, Permission.WRITE)), 'administrator': ('管理员', (Permission.FOLLOW, Permission.COMMENT, Permission.WRITE, Permission.ADMIN)), } default_role = 'reader' for r in roles: # r 是字典的键,比如 'reader' role = Role.query.filter_by(slug=r).first() if role is None: role = Role(slug=r, name=roles[r][0]) role.reset_permissions() for perm in roles[r][1]: role.add_permission(perm) role.default = (role.slug == default_role) db.session.add(role) db.session.commit() def reset_permissions(self): self.permissions = 0 def has_permission(self, perm): return self.permissions & perm == perm 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 __str__(self): return self.name def __repr__(self): return '<Role {}>'.format(self.name)
类方法 insert_roles()
并不直接创建新角色对象,而是通过角色 slug
查找现有的角色,然后再进行更新。只有当数据库中没有某个角色名时才会创建新角色对象。如此一来,如果以后更新了角色列表,就可以执行更新操作了。要想添加新角色,或者修改角色的权限,修改 roles
数组,再运行函数即可
最后,让我们修改 User
模型,指定与角色的关系:
class User(PaginatedAPIMixin, db.Model): ... # 用户所属的角色 role_id = db.Column(db.Integer, db.ForeignKey('roles.id')) ...
1.2 数据库迁移
修改数据模型后要使用 Flask-Migrate
进行数据库迁移:
(venv) D:\python-code\flask-vuejs-madblog\back-end>flask db migrate -m "add roles table" (venv) D:\python-code\flask-vuejs-madblog\back-end>flask db upgrade
1.3 初始化角色表
若想把角色写入数据库,可使用 flask shell
会话,首先需要修改 back-end/madblog.py
,在 shell 上下文中增加 Role
:
from app.models import Role ... @app.shell_context_processor def make_shell_context(): return {'db': db, 'Role': Role, 'User': User, 'Post': Post, 'Comment': Comment, 'Notification': Notification, 'Message': Message}
然后执行 flask shell
命令:
(venv) D:\python-code\flask-vuejs-madblog\back-end>flask shell Python 3.6.1 (v3.6.1:69c0db5, Mar 21 2017, 17:54:52) [MSC v.1900 32 bit (Intel)] on win32 App: app [production] Instance: D:\python-code\flask-vuejs-madblog\back-end\instance >>> Role.insert_roles() >>> Role.query.all() [<Role 小黑屋>, <Role 读者>, <Role 作者>, <Role 管理员>]
1.4 赋予角色
现在新注册一个用户时,会被赋予默认的角色 reader
。但是,你需要想一下,如果你是第一次部署该系统时,首先会创建管理员账号,而我们在 back-end/config.py
中已经配置了 ADMINS
变量,如果用户注册时的邮箱在 ADMINS
对应的列表中,则自动将这个用户的角色提升为 administrator
注意:
因为我们创建新用户的 API 中是使用 User.from_dict(self, data, new_user=True)
,所以不能把自动分配用户角色的逻辑代码放到 User.__init__()
中,那样的话会先执行 __init__()
,而此时 self.email
永远是 None
,所以下面的代码是错误的:
0 条评论
评论者的用户名
评论时间暂时还没有评论.