Flask權限管理

權限管理功能的實現能夠分爲如下幾個小塊:
1,新建數據庫表Role,裏面包括id(Integer,主鍵)name(String),permission(Integer),default(boolean)。users是指向User模型的對外關係,反向賦給User模型一個role屬性,這樣就能夠同郭User.role來訪問Role模型,這樣就建立了數據庫之間的關係。模型裏面還定義了一個靜態方法(@staticmethod,能夠直接經過類訪問這個方法而不須要建立實例以後才能訪問這個方法),它的做用是初始化Role數據表中的數據,數據庫模型代碼以下:html

class Role(db.Model):
    # 定義Role的數據庫模型
    __tablename__ = 'roles'
    id = db.Column(db.Integer, primary_key=True)
    # 該用戶角色名稱
    name = db.Column(db.String(64), unique=True)
    # 該用戶角色對應的權限
    permissions = db.Column(db.Integer)
    # 該用戶角色是否爲默認
    default = db.Column(db.Boolean, default=False, index=True)
    # 角色爲該用戶角色的全部用戶
    users = db.relationship('User', backref='role', lazy='dynamic')

    @staticmethod
    def insert_role():
        roles = {
            'STAFF': (Permission.ONLY_QUERY, True),
            'HIGH_STAFF': (Permission.ONLY_QUERY|
                       Permission.FORBID, False),
            'LEADER': (Permission.ONLY_QUERY|
                       Permission.FORBID|
                       Permission.ASSIGN, False),
            'ADMINISTATOR': (0x0f, False)
        }#除了onlyquery以外,其餘的都是模式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

 

Permission類代碼以下:數據庫

class Permission:
    ONLY_QUERY = 0x01#僅查詢
    FORBID = 0x03#封號
    ASSIGN= 0x07#分配行號
    ADMINISTRATOR = 0x0f#這個權限要異或

2,授予用戶權限:在User模型中添加role的屬性flask

class User(UserMixin,db.Model):
    __tablename__ = 'users'
    id = db.Column(db.SmallInteger,primary_key=True,nullable=False)#這個字段必須是id,不然沒法完成登陸驗證
    username = db.Column(db.String(64))
    password_hash = db.Column(db.String(128))
    email = db.Column(db.String(64))
    role_id = db.Column(db.Integer, db.ForeignKey('roles.id'))
    config_content = config[os.getenv('FLASK_CONFIG') or 'default']

由於User模型有role的屬性,能夠經過User.role來獲取Role數據庫中的內容,因此咱們的思路是直接經過這一特性進行操做,直接在User模型中的初始化方法中實現默認權限的賦予,是管理員給管理員權限,不是給默認的用戶權限。session

    def  __init__(self,**kwargs):
        super(User,self).__init__(**kwargs)
        #繼承了父類的初始化方法,super等價於UserMixin
        self.role = kwargs['role_id']
        if self.role is None:
            if self.email == self.config_content.ADMINS:
            #驗證email是否爲設置的管理員的email
                self.role = Role.query.filter_by(permissions=0xff).first()
            if self.role is None:
            #若是通過了上一步權限還爲空,就給個默認的User權限
                self.role = Role.query.filter_by(default=True).first()

第4行是管理員爲普通員工註冊的時候提供的權限,若是是博客權限爲空的用戶,即剛註冊的用戶,能夠刪除第4行,如果用戶衝了一個尊貴的會員就須要單獨賦予權限了,因此咱們能夠在User模型裏建立一個修改權限的方法,須要的時候調用就能夠。 app

3,用它來對用戶進行限制: 
3.1,寫一個用來判斷用戶權限的方法,傳入用戶須要的權限,進行驗證,符合返回True,不然爲False。這個方法在User模型裏面:函數

    def can(self,permissions):
    #這個方法用來傳入一個權限來覈實用戶是否有這個權限,返回bool值
        return self.role is not None and\
        (self.role.permissions & permissions) == permissions

    def is_administrator(self):
    #由於經常使用因此單獨寫成一個方法以方便調用,其它權限也能夠這樣寫
        return self.can(Permission.ADMINISTRATOR)

3.2 將上面方法寫入修飾函數中ui

#encoding:utf8

from functools import wraps
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)
            return f(*args, **kwargs)
        return decorated_function
    return decorator

def admin_required(f):
    return permission_required(Permission.ADMINISTRATOR)(f)

3.3 用修飾函數對有權限要求的路由進行修飾:url

@main.route('/forbid',methods=['GET','POST'])
@login_required
@permission_required(Permission.FORBID)
def forbid():
    return 'Hello World'

4新用戶註冊:spa

在view中添加以下代碼:code

##註冊
@main.route('/register',methods=['GET','POST'])
@login_required
@permission_required(Permission.ASSIGN)
def register():
    form = RegisterationForm()
    if form.validate_on_submit():
        register_judge_obj = register_judge(form.permission.data)
        role_id = register_judge_obj.judge()
        user = User(username=form.username.data,password=form.password.data,email=form.email.data,role_id = role_id)
        db.session.add(user)
        db.session.commit()
        return redirect(url_for('main.login'))
    return render_template('register.html',form=form)

其中調用的register_judge類代碼爲

# -*- coding: utf-8 -*-
from app.models import Role

class register_judge:

    def __init__(self,permission_id):
        self.permission_id = permission_id

    def judge(self):
        if self.permission_id == '1':
            role_id = Role.query.filter_by(id=4).first()
        elif self.permission_id == '2':
            role_id = Role.query.filter_by(id=3).first()
        elif self.permission_id == '3':
            role_id = Role.query.filter_by(id=1).first()
        else:
            role_id = Role.query.filter_by(default=True).first()
        return role_id

實際操做過程當中遇到的坑: 

1,權限存入數據庫中數值改變:它實際上是以2進制的方式來處理的

    0b00000001
    0b00000010
    0b00000100
    0b10000000
若是隻是第一個權限,它的值爲1
若是是第二個權限(0b00000001|0b00000010)即0b00000011,即3
第三個(0b00000010|0b00000010|0b00000010)即0b00000111即7
以此類推
它並非以10進制來計算的

2,裝飾函數的理解存在問題。

相關文章
相關標籤/搜索