Flask 权限设计 数据库+装饰器

宏观层面权限组合:read/update/create/delete

创建数据库

# 角色库与用户库一一对应
c.execute('''
CREATE TABLE "role" (
  "id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
  "name" varchar(64) NOT NULL DEFAULT '',
  "default" Boolean NOT NULL DEFAULT FALSE,
  "permission" integer
);
''')

c.execute('''
CREATE TABLE "user" (
  "id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
  "name" varchar(64) NOT NULL DEFAULT '',
  "password" varchar(64) NOT NULL DEFAULT '',
  "status" tinteger unsigned NOT NULL DEFAULT '1',
  "last_login_ip" varchar(64) DEFAULT NULL,
  "last_login_time" datetime DEFAULT NULL,
  "create_time" datetime  DEFAULT (datetime('now', 'localtime')),
  "update_time" datetime  DEFAULT (datetime('now', 'localtime')),
  "role_id" integer,
  FOREIGN KEY("role_id") REFERENCES role(id)
);
''')

创建对应的模型类

class User(db.Model):
    __tablename__ = "user"
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String, default="")
    password = db.Column(db.String, default="")
    status = db.Column(db.Integer, default=1)
    last_login_ip = db.Column(db.String)
    last_login_time = db.Column(db.DateTime)
    create_time = db.Column(db.DateTime, default=datetime.now)
    update_time = db.Column(db.DateTime, default=datetime.now)
    role_id = db.Column(db.Integer, db.ForeignKey('role.id'))

# 权限常量
class Permission:
    READ = 0x01
    CREATE = 0x02
    UPDATE = 0x04
    DELETE = 0x08
    DEFAULT = READ

# 角色表
class Role(db.Model):
    __tablename__ = 'role'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(64),default="")
    default = db.Column(db.Boolean, default=False)
    permission = db.Column(db.Integer)
    user = db.relationship('User', backref='role', lazy='dynamic')
    
    # 生成2个默认角色
    @staticmethod
    def insert_role():
        roles = {
            'user':(Permission.READ | Permission.UPDATE, True),
            'admin':(Permission.READ | Permission.UPDATE | Permission.CREATE |
                     Permission.DELETE, False)
        }
        for r in roles:
            role=Role.query.filter_by(name=r).first()
            if role is None:
                role = Role(name=r)
                role.permission=roles[r][0]
                role.default = roles[r][1]
                db.session.add(role)
        db.session.commit()

权限认证

def permission_required(permission):
    def decorator(func):
        @wraps(func)
        def decorated_function(*args, **kwargs):
            if 'FREE_LOGIN_WHITELIST' in current_app.config and request.remote_addr in current_app.config['FREE_LOGIN_WHITELIST']:
                return func(*args, **kwargs)

            user_info = session.get("user")
            if user_info:
                user = User.query.get(user_info["id"])
                if user.status in [0, 2]:
                    session.pop("user", None)
                    return success_response("notlogin", {}), 401
                # else:
                #     return func(*args, **kwargs)
            else:
                return success_response("notlogin", {}), 401

            user_permission = user.role.permission

            # 判断用户是否具有某特定权限,如果没有则抛出403错误
            if user_permission & permission == permission:
                return func(*args, **kwargs)
            else:
                return success_response("notpermission", {}), 403

        return decorated_function
    return decorator

使用装饰器实现权限验证

@dashbord_blueprint.route('/dashboard/web_risk_index', methods=['GET'])
@permission_required(Permission.READ)
def web_risk_index():

接口层面权限版本:permission_lists

数据库模型设计

# user表
class User(db.Model):
    __tablename__ = 'sys_users'
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(50), unique=True)
    passwd_hash = db.Column(db.String(128), nullable=False)

    # 非持久化字段,用于user关联其角色信息
    roles = []
    # 非持久化字段,用于在current_user中保存权限列表,便于权限控制
    permissions = []

# 角色表
class Role(db.Model):
    __tablename__ = 'sys_roles'
    id = db.Column(db.Integer, primary_key=True)
    role_name = db.Column(db.String(50), unique=True, nullable=False)
    role_desc = db.Column(db.String(200))
  
# user和角色表
class UserRole(db.Model):
    __tablename__ = 'sys_user_role_rel'
    id = db.Column(db.Integer, primary_key=True)
    user_id = db.Column(db.Integer, nullable=False)
    role_id = db.Column(db.Integer, nullable=False)

# 权限表
class Permission(db.Model):
    __tablename__ = 'sys_permissions'
    id = db.Column(db.Integer, primary_key=True)
    # 权限名称,用于展示
    per_name = db.Column(db.String(50), unique=True, nullable=False)
    # 权限类型:[导航、菜单、按钮、接口]
    per_type = db.Column(db.String(20))
    # 权限编码,用于权限比对的关键字段,使用flask的url_for()的参数,比如admin.user
    per_code = db.Column(db.String(50), unique=True, nullable=False)
    # 父权限,用于构建权限树结构
    per_father = db.Column(db.Integer)

# 角色权限表 
class RolePermission(db.Model):
    __tablename__ = 'sys_role_permission_rel'
    id = db.Column(db.Integer, primary_key=True)
    role_id = db.Column(db.Integer, nullable=False)
    permission_id = db.Column(db.Integer, nullable=False)

权限资源的设计

# 示例数据 
per_name: u'网络风险指数'
per_type: u'接口'
per_code: 'api.web_risk_index'
per_father: 1

用户权限存放于可获取的变量中

role_ids = db.session.query(UserRole.role_id).filter(UserRole.user_id==user.id).all()
# 用户权限列表
per_codes=[]
if role_ids:
    for role_id in role_ids:
        # 获取角色权限
        per_ids = db.session.query(RolePermission.permission_id).filter(RolePermission.role_id == role_id[0]).all()
        if per_ids:
            for per_id in per_ids:
                # 将权限加到权限列表当中
                permission = Permission.query.filter_by(id=per_id[0]).first()
                per_codes.append(permission.per_code)

# 将权限列表放到session中缓存
per_codes = list(set(per_codes)
session['permissions'] = per_codes

根据权限进行控制

def role_required(text):
    def decorator(func):
        @wrapes(func):
        def wrapper(*args,**kwargs):
            if text not in session['user']['permissions']:
                return 403
            else:
                return func(*args,**kwargs)
        return wrapper
    return decorator

实际使用

@dashbord_blueprint.route('/dashboard/web_risk_index', methods=['GET'])
# 装饰器过滤
@permission_required(api.web_risk_index)
def web_risk_index():
    pass

你可能感兴趣的:(总结,学习)