[英]Flask-Socketio out of sync with Flask-Login's current_user
[英]Using Flask-SocketIO with Flask-Login and HTTP Basic Auth
我正在尝试实现一个非常简单的内部监控网页。 它应该显示一些数据,通过socketio实时更新。 服务器在后台运行一个线程,该线程获取数据并将其中继到客户端。
我想用登录表单保护页面。 为了简单起见,我选择了HTTP Basic Auth,主要是因为我不想设计登录表单。
我做了以下事情:
@login_manager.request_handler
,我检查request.authorization
。 如果它有效,我将返回一个经过身份验证的User
对象。 @login_manager.unauthorized_handler
,我触发验证对话框。 '/'
页面受@login_required
保护。 socketio.on('connect')
事件,并在那里检查current_user
。 如果未经过身份验证,我会断开连接。 这是整个工作示例:
## Standard imports, disregard them
import functools
import gevent
## Otherwise I'm getting KeyError on shutdown
import gevent.monkey
gevent.monkey.patch_all()
from flask import Flask, request, Response
from flask.ext.login import LoginManager, UserMixin, login_required, current_user
from flask.ext.socketio import SocketIO
## To see the logging.debug call in socketio.on('connect')
import logging
logging.getLogger().setLevel(logging.DEBUG)
## App configuration
app = Flask(__name__)
app.debug = True
app.config['SECRET_KEY'] = 'a long and random string'
login_manager = LoginManager()
login_manager.init_app(app)
socketio = SocketIO(app)
## This thing sends updates to the client
class BackgroundThread(gevent.Greenlet):
def run(self):
while True:
socketio.emit(
'my event',
{'my field': 'my data'},
namespace='/my-namespace'
)
gevent.sleep(2)
## Not bothering with a database
class User(UserMixin):
users = {
u'1': (u'myname', u'mypass')
}
def __init__(self, username, password):
self.username = username
self.password = password
def get_id(self):
return u'1'
@classmethod
def get_by_username(cls, requested_username):
for username, password in cls.users.itervalues():
if username == requested_username:
return User(username, password)
return None
## From https://flask-socketio.readthedocs.org/en/latest/
def authenticated_only(f):
@functools.wraps(f)
def wrapped(*args, **kwargs):
if not current_user.is_authenticated():
request.namespace.disconnect()
else:
return f(*args, **kwargs)
return wrapped
## The password is checked here
@login_manager.request_loader
def load_request(request):
auth = request.authorization
if auth is not None:
username, password = auth['username'], auth['password']
user = User.get_by_username(username)
if user is not None and user.password == password:
return user
return None
## From http://flask.pocoo.org/snippets/8/
@login_manager.unauthorized_handler
def http_basic_auth():
return Response(
'Could not verify your access level for that URL.\n'
'You have to login with proper credentials', 401,
{'WWW-Authenticate': 'Basic realm="Login Required"'})
@app.route('/')
@login_required
def index():
return "My page" # in real code this is actually a render_template call
@socketio.on('connect', namespace='/my-namespace')
@authenticated_only
def test_connect():
logging.debug('Client connected: {.username}.'.format(current_user))
if __name__ == '__main__':
thread = BackgroundThread()
thread.start()
socketio.run(app)
Flask-Login
文档强调要实际登录用户,我必须显式调用login_user
。 我不这样做,但我可以登录。这怎么可能? UPD:在可预见的未来,我将成为唯一的用户,因此大多数情况下我担心是否可以拦截和解密流量,或者通过Websocket连接发送数据而不进行身份验证。
如果我使用带有自签名证书的HTTPS,这个设置是否安全?
你有用户密码存储在你的数据库中的纯文本(我知道,你还没有数据库,但我想你最终会有一个?)。 如果您的数据库被黑客入侵,那么您的用户会讨厌您,特别是那些使用相同密码进行在线银行业务的用户。 您应该在数据库中存储散列密码,以防止黑客入侵。 查看Flask-Bcrypt或Werkzeug中的密码散列函数。
使用HTTPS很好,但由于您还使用WebSocket,因此需要评估通过套接字连接的数据是否也需要加密。
自签名证书不是一个好主意,因为浏览器无法验证其真实性,因此他们(正确地)建议您的用户远离您的网站。
Flask-Login文档强调要实际登录用户,我必须显式调用login_user。 我不这样做,但我可以登录。这怎么可能?
记录用户的想法是,您不必对发送的每个请求重新对其进行身份验证。 login_user
只记录用户登录session
。 在后续请求中,Flask-Login将在会话中找到用户,因此不需要再调用您的回调来进行身份验证。
在您的情况下,您正在使用HTTP基本身份验证。 浏览器将在每个请求中发送Authorization
标头,并且由于Flask-Login从未在session
找到任何内容,因此它始终会调用您的回调,该回调每次都会对用户进行身份验证。 我没有看到任何问题,但是如果你想避免不断地对用户进行身份验证的工作(特别是在你添加密码哈希之后,这是CPU密集型的),你可能要考虑调用login_user
函数来制作一个更有效率。
更新 :因此您声称您计划在代码中保留以纯文本格式写的用户列表。 这真是个糟糕的主意。 您希望努力使数据在客户端和服务器之间保持安全,因此您还应该在存储密码方面采取良好的安全措施。
我看到在您是唯一用户的小型网站的代码中使用密码的最大风险是您错误地公开了代码。 例如,如果您希望将代码置于版本控制之下,那么除了在服务器上运行的副本(可以被黑客攻击的另一个位置)之外,您还将拥有密码的副本。 如果你也备份你的脚本,它也会在那里。
所以请帮个忙,不要在代码中写密码。 至少,在启动时从环境变量中读取它。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.