Flask 学习 八 用户角色

 角色在数据库中表示

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)
    default = db.Column(db.Boolean,default=False,index=True) # 默认角色
    permissions = db.Column(db.Integer)# 位标志,各个操作都对应一个位位置,能执行某项操作的角色,其位会被设为1
    users = db.relationship('User',backref='role',lazy='dynamic')# 不加载记录,但是提供加载记录的查询

程序的权限 里面的位值。

权限常量app/models.py

class Permission:
    FOLLOW=0x01
    COMMENT=0x02
    WRITE_ARTICLES=0x04
    MODERATE_COMMENTS=0x08
    ADMINISTER=0x80

用户角色

使用权限组织角色(不同角色拥有的权限利用位运算进行组合)

在Role类中写一个insert_roles方法来完成将角色添加到数据库中的操作

@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() # 提交数据库

赋予角色

app/models.py

管理员的管理员角色保存在设置变量FLASK_ADMIN中的电子邮件地址识别,只要这个电子邮件出现注册请求,就会被正确赋予角色

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(permissions=0xff).first() # 设置其为管理员
            if self.role is None: # 或者设置为默认角色
                self.role=Role.query.filter_by(default=True).first()

角色认证

为了简化角色和权限的实现过程,在User中添加一个辅助方法,检查是否有正确权限

from flask_login import UserMixin,AnonymousUserMixin # 匿名用户角色
class User(UserMixin,db.Model):
    #。。。。
    
    def can(self,permissions):
        # 在请求和赋予角色这两种权限进行位的“与”运算,如果成立,则允许用户执行此项操作
        return self.role is not None and \
               (self.role.permissions & permissions)==permissions 
    def is_administrator(self):# 认证为管理员角色判断
        return self.can(Permission.ADMINISTER)
class AnonymousUser(AnonymousUserMixin):
    def can(self, permissions):
        return False
    def is_administrator(self):
        return False
# 用户未登录时current_user的值,并且不用用户登陆即可检查用户权限
login_manager.anonymous_user=AnonymousUser

让视图函数只对具有特定权限的用户开放,可以自定义装饰器,一个检查常规权限,一个检查管理员权限

app/decorators.py

from flask import abort
from flask_login import current_user
from app.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) # 如果不具备该权限,返回403错误码
            return f(*args,**kwargs)
        return decorated_function
    return decorator

def admin_required(f):
    return permission_required(Permission.ADMINISTER)(f)
# 举例演示使用权限检查装饰器
from app.decorators import admin_required,permission_required
from ..models import Permission
from flask_login import login_required

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

@main.route('/moderator')
@login_required
@permission_required(Permission.MODERATE_COMMENTS)
def for_moderator_only():
    return 'For comment moderator'

在模板中也需要检查权限,所以Permission类为所有 位 定义了常量以便获取,为了避免每次调用render_template()多添加一个模板参数,可以使用上下文管理器,让变量在所有模板中全局访问

app/main/__init__.py

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

新添加角色和权限在单元测试中测试

test/test_user_model.py

class UserModelTestCase(unittest.TestCase):
    def test_roles_and_permissions(self):
        Role.insert_roles()
        u = User(email='join12@example.com',password='cat')
        self.assertTrue(u.can(Permission.WRITE_ARTICLES))
        self.assertFalse(u.can(Permission.MODERATE_COMMENTS))
    def test_anonymous_user(self):
        u=AnonymousUser()
        self.assertFalse(u.can(Permission.FOLLOW))

 

posted @ 2017-05-23 07:48  Erick-LONG  阅读(457)  评论(0编辑  收藏  举报