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)
    users = db.relationship('User', backref='role', lazy='dynamic')
    

 

程序的权限

FOLLOW         关注用户              0x01

COMMET         在他人文章中发表评论  0x02

WRITE_ARTICLES     写文章         0x04

MODERATE_COMMENTS    管理他人发表的评论   0x08

ADMINISTER        管理员权限       0x80

 

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

列出了要支持的用户角色以及定义角色使用的权限位

 

用户角色

匿名        0x00      未登录的用户,在程序中只有阅读权限

用户        0x07      具有发表文章,发表评论和关注其他用户的权限。这是新用户的默认角色

协管员       0x0f       增加审查不当评论的权限

管理员       0xff       具有所有权限,包括修改其他用户所属角色的权限

 

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)
    users = db.relationship('User', backref='role', lazy='dynamic')

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

这个Role表添加了一个静态方法,执行insert_roles函数会创建三个name,分别是用户,协管员和管理员

 

赋予角色:

 

app/models.py

class User(UserMixin,db.Model):
    __tablename__ = 'users'
    id = db.Column(db.Integer, primary_key=True)
    email = db.Column(db.String(64),unique=True,index=True)
    username = db.Column(db.String(64), unique=True,index=True)
    role_id = db.Column(db.Integer,db.ForeignKey('roles.id'))
    password_hash = db.Column(db.String(128))

    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(permission=0xff).first()
            if self.role is None:
                self.role = Role.query.filter_by(default=True).first()

User表的方法是:如果用户没有设置权限,用户的邮箱等于配置中设置的管理员邮箱,就设置为管理员权限,用户的权限为空,则设置为普通用户

 

角色验证

app/models.py:检查用户是否有指定的权限

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)

can()方法在请求和赋予角色这两种权限之间进行位与操作。如果角色中包含请求的所有权限位,则返回True ,表示允许用户执行此项操作。

is_administrator()方法用来检车管理员权限

from flask_login import AnonymousUserMixin

class AnonymousUser(AnonymousUserMixin): def can(self,permissions): return False def is_administraror(self): return False login_manager.anonymous_user = AnonymousUser

 这个类用来检查匿名用户的权限

 

app/decorators.py:检查用户权限的自定义修饰器

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)

这两个修饰器都使用了Python标准库中的functools包,如果用户不具有指定权限,则返回403错误码,即HTTP“禁止”错误。要添加一个403错误页面

 

以下的例子就是将上面的装饰器,用在了路由功能里面,针对一些页面设置了权限

from decorators import admin_required, permission_required  
from .models import Permission  
@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_moderators_only():  
    return "For comment moderators!"  

  

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

 

app/main/__init__.py:把Permission类加入模板上下文

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

 

tests/test_user_models.py:角色和权限的单元测试

#...

    def test_roles_and_permissions(self):
        Role.insert_roles()
        u = User(email='1808863623@qq.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-08-25 10:32  换季  阅读(2743)  评论(0编辑  收藏  举报