9

我正在使用 Flask Login 和 Principal 进行身份和角色管理。我的需求是直接从文档中描述的。我的代码在这里:

@identity_loaded.connect_via(app)
def on_identity_loaded(sender, identity):
    # Set the identity user object
    identity.user = current_user

    # Add the UserNeed to the identity
    if hasattr(current_user, 'get_id'):
        print 'current_user ' + str(current_user.get_id())
        identity.provides.add(UserNeed(current_user.get_id))

    # Assuming the User model has a list of roles, update the
    # identity with the roles that the user provides
    if hasattr(current_user, 'roles'):
        if current_user.roles:
            for role in current_user.roles:
                identity.provides.add(RoleNeed(role.name))

在我的登录代码中,我这样做:

identity_changed.send(current_app._get_current_object(),
                                  identity=Identity(user.user_id)

登录时,信号按预期触发。在随后的每次页面加载中,current_user 是匿名的并且没有用户 ID,但所有 @login_required 函数的行为就像用户已登录一样。Flask login 知道用户已登录,但由于某种原因 current_user 不一致。

我是否在某处遗漏了配置的基本点?

4

2 回答 2

7

我遇到了同样的问题!根本原因是 Flask-Login 和 Flask-Principal 都是 Flask 在请求的“预处理”阶段按照它们在 Flask app 中注册的顺序调用的。如果在注册 Flask-Login 之前注册了 Flask-Principal,那么@identity_loaded.connect_via(app)会在之前调用@login_manager.user_loader,因此 current_user 会返回匿名用户。

Flask-Principal 文档示例显示了Flask-Principal在Flask-Login之前注册的代码摘录。啧啧!这是我最终在引导程序中所做的事情:

login_manager = LoginManager()
login_manager.init_app(app)

# ...

principals = Principal(app) # This must be initialized after login_manager.

然后在我的 users.py 视图文件中:

@identity_loaded.connect_via(app)
def on_identity_loaded(sender, identity):
    """ This function is called by Flask-Principal after a user logs in. """

    identity.user = current_user

    if isinstance(current_user, User):
        identity.provides.add(UserNeed(current_user.id))

    for permission in user.permissions:
        # Do permission-y stuff here.

这为我解决了这个问题。

编辑:我向项目提交了一份错误报告以获取文档。

于 2013-09-21T18:13:22.203 回答
1

谢谢你,这是一个相关的观察,以防它有用。我一直在努力解决用户角色在登录后的后续请求中没有持续存在的类似问题。作为一个初学者,玩不同的东西,比如烧瓶用户(我从来没有工作过),并选择 a)烧瓶主体和烧瓶登录和 b)烧瓶导航而不是烧瓶导航。

这样我就可以 1) 轻松控制基于 Principal 显示的菜单项和 2) 避免在模板之外生成导航标记(因为我总是被教导要分离逻辑和表示并为 flask-nav 编写自定义菜单渲染器如果我想稍后更改 HTML,只是更改周围的 HTML 似乎不正确)。我找不到迭代烧瓶导航对象或向导航项目添加自定义属性的方法,而在烧瓶导航中,我正在创建一个自定义项目,扩展烧瓶导航的项目以添加所需的权限。

我还试图解决的挑战是具有角色层次结构,以防止必须在我的视图中放置复杂的权限语句(例如,管理员也是编辑器,也是用户,也是匿名用户等。)找到任何类似的层次结构概念。我也不想在我的模型中为一个用户分配多个角色。

我的错误是:

  • 以上按加载顺序
  • 由于我还没有将角色放入我的模型中,因此用户和角色之间存在多对多关系,在我的无知中,我没有意识到 Flask-Login 需要在 @login_manager.user_loader 函数中加载角色,如果模型中还没有的角色。我改为在 login_user(user) 之后在登录视图中分配角色,然后再发出flask-principal信号。
  • 用我不同的方法分配了角色,但在下一个请求中忘记了。这篇文章给了我正在寻找的线索。这就是我最终要做的——所有其他与 Principal 相关的代码都与文档及以上内容相同。
#CAVEATS - still learning Flask so this may not be the right approach and it is still a W.I.P.
#added a  kind of hierarchy order field to User to drive multiple roles in Permissions
#with this model I only have to assign one role to a user

class Role(db.Model):
    __tablename__ = 'roles'
    id = db.Column(db.Integer(), primary_key=True)
    name = db.Column(db.String(50), unique=True)
    description = db.Column(db.String(200))
    hierarchy_order = db.Column(db.Integer)
    internal = db.Column(db.Boolean) # this is only so I can identify registered users and internal users
    users = db.relationship('User', backref='role',lazy='dynamic')

    def __repr__(self):
        return '<Role: {}>'.format(self.name)

# changed common flask-login example @login_manager.user_loader as follows

@login_manager.user_loader
def load_user(user_id):
    user = User.query.get(int(user_id))
    #work out what roles are below current role
    permissable_roles = Role.query.filter(Role.hierarchy_order<=user.role.hierarchy_order).all()
    user.roles = permissable_roles
    return user

我非常希望将这种方法作为一种通用约定,但我认为我坚持在@login_manager.user_loader 中有一个循环,它将多个角色分配为从分配的角色向下延伸的层次结构。我希望其中的一些可以帮助那些挣扎于如何将这一切联系在一起的人。关于烧瓶存储东西的位置以及它们何时在不同的环境中可用,我还有很多东西要学习。

于 2019-02-01T12:39:42.737 回答