如何使用烧瓶安全保护烧瓶管理面板

Posted

技术标签:

【中文标题】如何使用烧瓶安全保护烧瓶管理面板【英文标题】:How to secure the flask-admin panel with flask-security 【发布时间】:2015-09-14 11:55:57 【问题描述】:

我希望保护使用 Flask 制作并与 flask-admin 集成的 Web API 以提供管理界面。我搜索并发现flask-admin 在/admin 有一个管理面板,默认情况下任何人都可以访问它。它不提供身份验证系统并且完全开放(没有任何安全性),因为他们没有假设将使用什么来提供安全性。这个 API 必须在生产中使用,所以我们不能为每个点击 url 的人提供一个开放的/admin 路由。需要正确的身份验证。

views.py 中,我不能简单地放置/admin 路由并通过装饰器提供身份验证,因为这会覆盖flask-admin 已经创建的现有路由,从而导致错误。

进一步研究表明有两个模块flask-adminflask-security。我知道flask-adminis_accessible 方法来保护它,但它没有提供flask-security 提供的太多功能。

我没有找到任何方法来保护端点 /admin 以及以 /admin 开头的所有其他端点,例如 /admin/<something>

我正在寻找专门使用烧瓶安全性来完成这项任务。如果不可能,请提出替代方案。

PS:我知道我可以锁定ngnix 本身,但那将是最后的选择。如果我可以通过flask-security 拥有一个身份验证系统,那就太好了。

【问题讨论】:

Flask-Security 的认证机制(通过 Flask-Login)控制current_user.is_authenticated() 的返回值。在你的is_accessible 实现中返回这个(可能结合某种角色/权限检查)应该让你能够在 Flask-Admin 中使用 Flask-Security 的保护。 【参考方案1】:

由于这是“flask-security 安全管理员”谷歌搜索的第一个结果,而且还没有开箱即用的解决方案,我想我可以贡献一下。

在flask-admin 项目Issue List 上提出了一个类似的问题,并提供了一个使用flask-login 和mogodb 的简单示例here。

我使用 SQLAchemy 为 sqlite 数据库和烧瓶安全做了一个示例。请参阅下面的示例烧瓶应用程序:

 #!/usr/bin/env python
# -*- coding: utf-8 -*-

import os
import os.path as op
from flask import Flask, render_template, url_for, request
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy.event import listens_for
from flask.ext.security import current_user, login_required, RoleMixin, Security, SQLAlchemyUserDatastore, UserMixin
from flask_admin import Admin, AdminIndexView
from flask_admin.contrib import sqla

# Create application
app = Flask(__name__)

# Create dummy secrety key so we can use sessions
app.config['SECRET_KEY'] = '123456790'

# Create in-memory database
app.config['DATABASE_FILE'] = 'sample_db.sqlite'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + app.config['DATABASE_FILE']
app.config['SQLALCHEMY_ECHO'] = True
db = SQLAlchemy(app)

# Create directory for file fields to use
file_path = op.join(op.dirname(__file__), 'static/files')

# flask-security models

roles_users = db.Table('roles_users',
        db.Column('user_id', db.Integer(), db.ForeignKey('user.id')),
        db.Column('role_id', db.Integer(), db.ForeignKey('role.id')))

class Role(db.Model, RoleMixin):
    id = db.Column(db.Integer(), primary_key=True)
    name = db.Column(db.String(80), unique=True)
    description = db.Column(db.String(255))

class User(db.Model, UserMixin):
    id = db.Column(db.Integer, primary_key=True)
    email = db.Column(db.String(255), unique=True)
    password = db.Column(db.String(255))
    active = db.Column(db.Boolean())
    confirmed_at = db.Column(db.DateTime())
    roles = db.relationship('Role', secondary=roles_users,
                            backref=db.backref('users', lazy='dynamic'))

# Create Security
user_datastore = SQLAlchemyUserDatastore(db, User, Role)
security = Security(app, user_datastore)

# Only needed on first execution to create first user
#@app.before_first_request
#def create_user():
#    db.create_all()
#    user_datastore.create_user(email='yourmail@mail.com', password='pass')
#    db.session.commit()

class AnyModel(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.Unicode(64))

    def __unicode__(self):
        return self.name

class MyAdminIndexView(AdminIndexView):
    def is_accessible(self):
        return current_user.is_authenticated() # This does the trick rendering the view only if the user is authenticated

# Create admin. In this block you pass your custom admin index view to your admin area 
admin = Admin(app, 'Admin Area', template_mode='bootstrap3', index_view=MyAdminIndexView())


# Add views
admin.add_view(sqla.ModelView(AnyModel, db.session)) 

# To acess the logout just type the route /logout on browser. That redirects you to the index
@login_required
@app.route('/login')
def login():
    return redirect('/admin')

@app.route('/')
def index():
    return render_template('index.html')


if __name__ == '__main__':

    # Build sample db on the fly, if one does not exist yet.
    db.create_all() 
    app.run(debug=True)

请参阅 flask-security 文档以了解 how to customize the login page。

希望这会有所帮助。

【讨论】:

这非常有用。默认行为对我来说毫无意义。我不明白您为什么希望未经身份验证的用户能够任何访问您的管理面板。 您的代码可以工作,但不会自动重定向到登录,以防用户未通过身份验证,官方文档提供了一种实现方式:flask-admin.readthedocs.io/en/latest/introduction/… 用于自动重定向到登录添加到 MyAdminIndexView 类方法:def inaccessible_callback(self, name, **kwargs): return redirect(url_for('login', next=request.url))【参考方案2】:

您应该查看Flask-Security-Admin 项目,我认为它非常清楚地涵盖了您正在寻找的内容。

直接取自上面的链接:

当您第一次访问该应用的主页时,系统会提示您登录,这要归功于 Flask-Security。 如果您使用 username=someone@example.com 和 password=password 登录,您将拥有“最终用户”角色。 如果您使用 username=admin@example.com 和 password=password 登录,您将拥有“admin”角色。 任何一个角色都可以访问主页。 任何一个角色都可以访问 /admin 页面。但是,除非您具有“管理员”角色,否则您不会在此页面上看到用于管理用户和角色的选项卡。 只有 admin 角色可以访问 /admin 页面的子页面,例如 /admin/userview。否则,您将收到“禁止”响应。 请注意,在编辑用户时,由于 Flask-Admin,角色的名称会自动填充。 您可以添加和编辑用户和角色。生成的用户将能够登录(除非您设置 active=false),并且如果他们具有“admin”角色,则能够执行管理。

相关代码位于 main.py 中,并明确注释说明如何复制使用 flask-security 保护 flask-admin 面板的过程。

与您相关的最基本的部分如下(第 152 行):

# Prevent administration of Users unless the currently logged-in user has the "admin" role
def is_accessible(self):
    return current_user.has_role('admin')

我希望这会有所帮助。

【讨论】:

谢谢!经过一些进一步的挖掘和研究,我得到了它的工作。 感谢您提供此信息。找了一个小时的答案,终于在这里找到了。【参考方案3】:

我对所有子视图使用@RamiMac 的答案,但对于索引之一(默认为/admin),我使用此方法,使用查看所需的admin 角色重新包装该方法。

@app.before_first_request
def restrict_admin_url():
    endpoint = 'admin.index'
    url = url_for(endpoint)
    admin_index = app.view_functions.pop(endpoint)

    @app.route(url, endpoint=endpoint)
    @roles_required('admin')
    def secure_admin_index():
        return admin_index()

在我的项目中,这直接位于我的所有 Flask-Admin 代码之后,它本身就在它自己的启动脚本 custom_flaskadmin.py 中。

【讨论】:

这很有趣,我从来没有见过 app.view_functions.pop 之前使用的方法。【参考方案4】:

Flask-Admin 文档中有关于安全性的部分:http://flask-admin.readthedocs.io/en/latest/introduction/#authorization-permissions

【讨论】:

在此处发布之前我已经对其进行了研究,在本节中,示例显示了使用flask-admin 而不是flask-security。我特别希望通过flask-security 来实现它,因为它提供了比flask-login 更多的功能。任何帮助将不胜感激。 @Joes ***.com/questions/46914054/… 文档也完全倒退了。您不必重写安全模板来扩展管理员模板,因为安全模板适用于管理员和非管理员...

以上是关于如何使用烧瓶安全保护烧瓶管理面板的主要内容,如果未能解决你的问题,请参考以下文章

使用JsonWebToken反应保护管理面板

带有烧瓶安全扩展的基于令牌的身份验证

Python 多处理管理器在烧瓶 API 中使用时显示错误

在烧瓶中保护 REST api

使用烧瓶保护静态文件 [重复]

等级保护测评策略建议整改措施