河源市金开元网络科技有限公司
首页 | 联系方式 | 加入收藏 | 设为首页 | 手机站

产品目录

联系方式

联系人:业务部
电话: 00172-849352
邮箱:service@oreude.com

当前位置:首页 >> 产品展示 >> 默认分类 >> 正文

Python Flask学习_用户角色和用户权限

详细信息:

在Web App中 ,存在和游客(匿名用户),普通用户,协管员,管理员等不同的角色,应当为不同的角色赋予不同的权限。各个用户只能在权限范围内访问页面、进行操作等。

通过一个例子来说明。本例中的用户分为4中:游客、普通用户、协管员、管理员。不同的用户有着不同的权限。

一、在数据库中表示不同的角色

权限有这么几种:


操作的权限使用8位二进制数表示,现在只用了5位,剩余的可以以后用来扩展。

权限的叠加是按位与运算,列如:拥有关注用户和写文章的权限应当是0b00000001&0b00000100,结果是0b00000101。

也容易反推出,0b00000011是0b00000001&0b00000010,因此权限是:关注用户和在他人文章中写评论。

用户的不同角色在数据库SQLite中存储,权限的位值(permission)作为角色的一个字段。

roles表的字段:


使用python代码实现:

# app/models.py
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')

在Role类中定义一个静态方法,提供将记录写入数据库的方法

# app/models.py
class Role(db.model):
 @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()

roles有三种:

'User':表示普通用户,权限值(permission)是(Permission.FOLLOW |Permission.COMMENT |Permission.WRITE_ARTICLES ,True),其中Permission是定义了各种权限的类(相当于C++中的枚举),default是True。

'Modetator':表示协管员,权限值也列出来了,default是False。

'Administrator':表示管理员,权限值是oxff,转化为二进制是0b11111111,表示拥有所有权限。default是False。

调用这个静态方法,会把三种角色和各自的权限值写入数据库中。

最后还有一个:游客,游客不需要登录,不需要在数据库中有表示。

以上完成后,可以使用shell对话,去写入数据库。


二、赋予角色

一个新用户注册以后,要给他赋予一个角色。看他是普通用户,还是协管员,还是管理员。、

新用户注册时,会实例化一个User类 ,然后调用db.session.add(u)和db.session.commit()直至提交到数据库。

在实例化User类时,根据邮箱判断角色。

# app/models
class User(UserMixin,db.Model):
 __tablename__ = 'users'
 id = db.Column(db.Integer, primary_key=True) #id列
 username = db.Column(db.String(64), unique=True, index=True) #username列
 role_id = db.Column(db.Integer, db.ForeignKey('roles.id')) #role_id列
 password_hash = db.Column(db.String(128)) #密码hash列
 email = db.Column(db.String(64),unique=True,index=True) #email列
 confirmed = db.Column(db.Boolean,default=False)
 def __init__(self,**kwargs):
 '''构造函数:首先调用基类构造函数,如果创建基类对象后没定义角色,则根据email地址决定其角色'''
 super(User,self).__init__(**kwargs)
 if self.role is None:
 if self.email == current_app.config['FLASKY_ADMIN']:
 self.role = Role.query.filter_by(permissions=0xff).first()
 if self.role is None:
 self.role = Role.query.filter_by(default=True).first()
role_id = db.Column(db.Integer, db.ForeignKey('roles.id')) #users表
users = db.relationship('User', backref='role', lazy='dynamic') #roles表
role_id是外键,和roles.id联结。可以通过u.role访问到联结的role对象。

self.email == current_app.config['FLASKY_ADMIN'] : 判断email为特定地址的话,就设定 self.role = Role.query.filter_by(permissions=0xff).first()。

三、验证角色

为了更好更快的验证当前角色有没有某些权限,就为User定义了一些方法。

# app/models.py
class User(UserMixin,db.models):
 def can(self,permissions):
 '''检查permissions要求的权限角色是否允许'''
 return self.role is not None and (self.role.permissions & permissions)==permissions
 def is_administrator(self):
 '''检查是否管理员'''
 return self.can(Permission.ADMINISTER)

self.role is not None and (self.role.permissions & permissions)==permissions:

要为True,需要满足两个条件:

1、role不为空,

2、role的权限值(self.role.permission)和要验证的权限(permission)的按位与操作值要和permission相等。

处于一致性考虑,还定义了AnonymousUserMixin类,并实现了can()方法和is_administrator()方法。再把它设为用户未登录时的current_user值。

# app/models.py
class AnonymousUser(AnonymousUserMixin):
 '''为了和User类保持一致,
 匿名登录时,使用current_user对象的类'''
 def can(self,permissions):
 return False
 def is_administrator(self):
 return False
login_manager.anonymous_user = AnonymousUser

用户分好了,不同用户的权限分好了,验证用户权限的方法也实现了。下面就是在视图函数中根据用户的不同 决定是否可以访问某些页面 。

首先,为了方便,定义了一些装饰器。

# app/decorators.py
'''定义装饰器'''
from functools import wraps
from flask import abort
from flask_login import current_user
from .models import Permission
def permission_required(permission):
 '''定义装饰器@permission_required(permission)'''
 def decorator(f):
 @wraps(f)
 def decorated_function(*args,**kwargs):
 if not current_user.can(permission): #如果当前用户不具有permission则抛出403错误。
 abort(403)
 return f(*args,**kwargs)
 return decorated_function
 return decorator
def admin_required(f):
 '''定义装饰器@admin_required'''
 return permission_required(Permission.ADMINISTER)(f)

注意:403.html也要实现一下。

然后,在视图函数中使用这些装饰器

# app/main/views.py
@main.route('/admin')
@login_required
@admin_required
def for_admin_only():
 '''测试:管理员权限'''
 role = 'admin'
 return render_template('for_permission_test.html',role=role)
@main.route('/moderator')
@login_required
@permission_required(Permission.MODERATE_COMMENTS)
def for_moderator_only():
 '''测试:协管员权限'''
 role = 'moderator'
 return render_template('for_permission_test.html',role=role)

/admin的URL只有拥有管理员权限的用户才能访问。

/moderator的URL也只有拥有协管(以上)权限的用户才能访问。


在模板中使用Permission类的方法。

还记得Permission类是定义了各种权限的值的吗?为了在模板中也能使用Permission类,可以使用上下文管理器

# app/main/__init__.py
@main.app_context_processor
def inject_permissions():
 '''使用上下文处理器,是变量在模板全局中可访问'''
 return dict(Permission=Permission)