權限管理功能的實現可以分為以下幾個小塊:
1,新建數據庫表Role,里面包括id(Integer,主鍵)name(String),permission(Integer),default(boolean)。users是指向User模型的對外關系,反向賦給User模型一個role屬性,這樣就可以同郭User.role來訪問Role模型,這樣就創建了數據庫之間的關系。模型里面還定義了一個靜態方法(@staticmethod,可以直接通過類訪問這個方法而不需要創建實例之后才能訪問這個方法),它的作用是初始化Role數據表中的數據,數據庫模型代碼如下:
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的屬性
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模型中的初始化方法中實現默認權限的賦予,是管理員給管理員權限,不是給默認的用戶權限。
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模型里創建一個修改權限的方法,需要的時候調用就可以。
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 將上面方法寫入修飾函數中
#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 用修飾函數對有權限要求的路由進行修飾:
@main.route('/forbid',methods=['GET','POST']) @login_required @permission_required(Permission.FORBID) def forbid(): return 'Hello World'
4新用戶注冊:
在view中添加如下代碼:
##注冊 @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,裝飾函數的理解存在問題。