问题描述
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上下文中以某种方式访问整个应用程序(包括常规的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扩展程序是一个不错的选择。