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))
