zoukankan      html  css  js  c++  java
  • 12、用户角色

    1、用户在数据库中的表示

    app/models.py  修改roles表,添加角色的权限

    只要有一个角色的defalut字段要设为True,其他都设为False。用户注册时,其角色会被设为默认角色

    添加了permissions字段,其值是一个整数,表示位标志,各个操作都对应一个位位置,能执行某项操作的角色,其位会被设为1

    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')
    
        def __repr__(self):
            return '<Role %r>' %self.name

    2、权限常量

    各个操作所需的程序权限是不一样的。Flask中,各个操作如下所示,其中,操作的权限使用8位表示,现在只用了其中5位,其他3位可用于将来的补充

    使用权限组织角色,在以后添加新角色时,只需执行不同的权限组合即可

    app/models.py   添加权限常量

    #权限常量
    class Permission:
        FOLLOW = 0x01
        COMMENT = 0x02
        WRITE_ARTICLES = 0x04
        MODERATE_COMMENTS = 0x08
        ADMINSTER = 0x80

    3、在数据库中使用创建角色

    将角色添加到手动添加到数据库既耗时又容易出错,作为替代,我们在Role类中添加一个类方法,完成这个操作

    app/models.py

    insert_roles()函数并不直接创建新角色对象,而是通过角色名查找现有角色,然后再更新,只有当数据库中没有该角色时才会创建,这样以后更新了列表,就可以执行更新操作了。要想添加新角色,或者修改角色的权限,修改roles数组,再运行函数即可。

    注:“匿名”角色不需要在数据库中表示出来,这个角色的作用就是为了表示不在数据库中的用户

    #定义数据库模型
    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()
    
        def __repr__(self):
            return '<Role %r>' %self.name

    若想把角色写入数据库,可使用shell会话:

    3、赋予角色

    app/models.py  定义默认的用户角色

    大多数用户在注册时赋予的角色都是“用户”,因为这是默认角色

    管理员在最开始就被赋予“管理员”角色

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

    class User(UserMixin, db.Model):
        __tablename__ = 'users'
        id = db.Column(db.Integer, primary_key=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))
        email = db.Column(db.String(64), unique=True, index=True)
        #确认用户账户
        confirmed = db.Column(db.Boolean, default=False)
        
        #定义默认的用户角色
        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()
        
        def generate_confirmation_token(self, expiration=3600):
            s = Serializer(current_app.config['SECRET_KEY'], expiration)
            return s.dumps({'confirm': self.id})
    
        def confirm(self, token):
            s = Serializer(current_app.config['SECRET_KEY'])
            try:
                data = s.loads(token)
            except:
                return False
            if data.get('confirm') != self.id:
                return False
            self.confirmed = True
            db.session.add(self)
            return True
    
        #在User模型中加入密码散列
        @property
        def password(self):
            raise AttributeError('password is not a readable attribute')
    
        @password.setter
        def password(self, password):
            self.password_hash = generate_password_hash(password)
    
        def verify_password(self, password):
            return check_password_hash(self.password_hash, password)
    
        def __repr__(self):
            return '<User %r>' %self.username

    4、角色验证

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

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

    User模型中添加的can()方法在请求和赋予角色这两种权限之间进行位与操作

    如果用户中包含请求的所有权限位,则返回True,表示允许用户执行此项操作。

    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.ADMINSTER) def generate_confirmation_token(self, expiration=3600): s = Serializer(current_app.config['SECRET_KEY'], expiration) return s.dumps({'confirm': self.id})
    class AnonymousUser(AnonymousUserMixin): def can(self, permissions): return False def is_administrator(self): return False login_manager.anonymous_user = AnonymousUser

    4、检查用户权限的自定义修饰器

    app/decorators.py

    这两个修饰器使用了functools包,如果用户不具有指定权限,则返回403错误,即HTTP“禁止”错误

    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 decorator
    
    def admin_required(f):
        return permission_required(Permission.ADMINSTER)(f)

    下面示例这些修饰器的使用方法

    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_permission():
        return dict(Permission=Permission)
  • 相关阅读:
    Python--面向对象编程
    Python--私有
    Python--格式化cookie为字典类型
    Python--异常处理
    Python--加密小练习
    bzoj 1774: [Usaco2009 Dec]Toll 过路费
    lougu T7983 大芳的逆行板载
    bzoj 1083(&vijos 1190): [SCOI2005]繁忙的都市 && bzoj 1601: [Usaco2008 Oct]灌水
    vijos 1083 小白逛公园
    51nod 1766 树上的最远点对
  • 原文地址:https://www.cnblogs.com/lw-monster/p/11809097.html
Copyright © 2011-2022 走看看