[英]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.