十二、用戶角色

一、用戶在數據庫中的表示shell

app/models.py  修改roles表,添加角色的權限數據庫

只要有一個角色的defalut字段要設爲True,其餘都設爲False。用戶註冊時,其角色會被設爲默認角色flask

添加了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

 

二、權限常量session

各個操做所需的程序權限是不同的。Flask中,各個操做以下所示,其中,操做的權限使用8位表示,如今只用了其中5位,其餘3位可用於未來的補充app

 

 

 

使用權限組織角色,在之後添加新角色時,只需執行不一樣的權限組合便可函數

app/models.py   添加權限常量ui

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

 

三、在數據庫中使用建立角色spa

將角色添加到手動添加到數據庫既耗時又容易出錯,做爲替代,咱們在Role類中添加一個類方法,完成這個操做code

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會話:

 

 

 

三、賦予角色

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

 

四、角色驗證

爲簡化角色和權限的實現過程,在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

 

四、檢查用戶權限的自定義修飾器

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)
相關文章
相關標籤/搜索