我正在尝试为内部监控实现一个非常简单的网页。它应该显示一些数据,这些数据通过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)
- 如果我使用带有自签名证书的 HTTPS,此设置是否安全?
Flask-Login
文档强调要实际登录用户,我必须明确调用login_user
. 我不这样做,但我可以登录。这怎么可能?
UPD:在可预见的未来我将成为唯一的用户,所以我最关心的是是否可以拦截和解密流量,或者通过 Websocket 连接发送数据而无需经过身份验证。