Flask-Socketio与Flask-Login的current_user不同步

问题描述

Python==3.8.5
Flask==1.1.2
Flask-SocketIO=4.3.0 (also tried 4.3.1)
Flask_Login=0.4.1

我在Gunicorn中使用gevent

gunicorn -k geventwebsocket.gunicorn.workers.GeventWebSocketWorker w 1 wsgi:server

我已经使用Flask-Login两年了,大约一年了,我设法使其与具有React前端的RESTful应用程序一起用作身份验证机制。我遇到的问题在于使用Flask-socketio。尽管我设法成功注销,但是flask_socketio端点中的current_user仍然将current_user对象保持为有效,而且当我与另一个用户重新登录时,更奇怪的是,全局Flask应用程序中的current_user反映了注销和新登录,但是flask_socketio中的current_user与更改失去同步。我一直在搜索很长一段时间,并且发现了flask_socketio的作者Miguel Grinberg的博客文章,他指出:

在Socket.IO连接时,将Flask会话复制到Socket.IO会话。 在建立Socket.IO连接之后,在Flask路由中对会话所做的更改将无法在Socket.IO会话上访问。 在Flask用户会话上将无法访问在Socket.IO事件处理程序中对会话所做的更改。

烧瓶登录支持: 您可以毫无问题地在Socket.IO事件处理程序中引用current_user。

的确可以从socket.io中引用和访问current_user,但这始终是初始值,并不反映其在使用期内可能发生的更改。

我可以在socket.io上下文中以某种方式访问​​整个应用程序(包括常规的http请求)可访问的current_user吗?

这是一个常规的http示例:

@auth_bp.route('/current_user/<int:id>')
def user_is_authenticated(id):
    if id:
        if current_user.is_authenticated:
            if current_user.id == int(id):
                return {
                            "code": 200,"authenticated": True,"message":"User is logged-in","user": user_schema.dump(current_user),},200
    return {
                "code": 400,"authenticated": False,"message":"Either user id missing or you are not authenticated.","user": None
            },400

如果我从客户端访问上述端点,则会得到 actual current_user。下面是用于相同目的的socketio端点:

@socket.on('getCurrentUser',namespace='/auth')
def user_is_authenticated(data):
    id = data.get('id')
    if id:
        if current_user.is_authenticated:
            if current_user.id == int(id):
                user = user_schema.dump(current_user)
                emit('currentUser',{'user': user,'room': user.room},room=user.room)
    #emit event here to sid

模拟身份验证操作

#login as Adam Clark

current_user across Flask Now is {
    fullname: 'Adam Clark',other_fields: 'data'
}

current_user in socketio context also is {
    fullname: 'Adam Clark',other_fields: 'data'
}

# logout

current_user across Flask Now is AnonymousUser

current_user in socketio context also is still {
    fullname: 'Adam Clark',other_fields: 'data'
}


# login as John Wild

current_user across Flask Now is {
    fullname: 'John Wild',other_fields: 'data'
}

current_user in socketio context also is still {
    fullname: 'Adam Clark',other_fields: 'data'
}

两者的唯一区别是http one(Flask端点)反映了current_user的当前状态,而socket.io仅保留了对象的初始状态。

解决方法

使用基于cookie的会话和Socket.IO时,这是一个不幸的限制。问题是,如果HTTP路由更改了会话(例如使用Flask-Login注销),则无法告诉WebSocket连接会话cookie已更改,因为WebSocket不支持cookie。在初始连接请求之外。

文档链接:https://flask-socketio.readthedocs.io/en/latest/#access-to-flask-s-context-globals

如果需要在HTTP和WebSocket端之间同步会话,请使用服务器端会话。 Flask-Session扩展程序是一个不错的选择。