权限管理功能的实现可以分为以下几个小块:
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 '' % 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:utf8from functools import wrapsfrom flask import abortfrom flask_login import current_userfrom app.models import Permissiondef 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 decoratordef 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 Roleclass 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,装饰函数的理解存在问题。