10.10. تسجيل الدخول إلى لوحة المعلومات¶
تحتاج لوحة معلومات الويب إلى نموذج تسجيل دخول -- فلا ينبغي أن يرى أشخاص عشوائيون على الشبكة المحلية الفناء. هذا هو دور الجلسات المدعومة بملفات تعريف الارتباط (cookies) ومُزخرِف تسجيل الدخول.
الجلسة عبارة عن قاموس صغير تكتبه الكاميرا داخل ملف تعريف ارتباط. يُوقَّع ملف تعريف الارتباط بمفتاح توقيع JWT السري الذي تم تحميله سابقًا في هذا الفصل، بحيث يمكن للمتصفح حمله معه ولكن لا يمكنه العبث بمحتوياته دون إبطال التوقيع.
10.10.1. إعداد كائنَي الجلسة وتسجيل الدخول¶
microdot.session.Session يثبّت آلية الجلسات على app. وmicrodot.login.Login يضيف المُزخرِف من نمط login_required والمساعدين login_user / logout_user:
# auth/login.py
from microdot.session import Session
from microdot.login import Login
Session(app, secret_key=SECRET,
cookie_options={'http_only': True, 'secure': False})
login = Login()
USERS = {
'owner': {'id': 'owner', 'password_hash': load_password_hash()},
}
@login.user_loader
async def load_user(user_id):
return USERS.get(user_id)
يمنع http_only=True شيفرة JavaScript الموجودة في الصفحة من قراءة ملف تعريف الارتباط -- وهو دفاع متعدد الطبقات ضد مهاجم XSS يحاول اختطاف الجلسة. أما secure=False فهو قيمة مؤقتة إلى أن يتم تفعيل HTTPS؛ غيّره إلى True بمجرد تشغيل الخادم عبر TLS بحيث لا ينتقل ملف تعريف الارتباط أبدًا عبر HTTP غير المشفر.
ملاحظة
البرمجة النصية عبر المواقع (XSS) هي فئة من الهجمات يتمكن فيها المهاجم من تنفيذ JavaScript داخل عرض المستخدم لصفحة موثوقة -- عادةً من خلال حقل نموذج غير مُهرَّب، أو تعليق مُعرَض بصيغة HTML، أو أداة طرف ثالث قابلة للاختراق. لا يمنع علم ملف تعريف الارتباط http_only هجمات XSS؛ بل يبقي ملف تعريف ارتباط الجلسة بعيدًا عن متناول السكربت المحقون فحسب، بحيث لا يمكن تحويل هجوم XSS الناجح إلى اختطاف للجلسة بسهولة.
يُستدعى user_loader عند كل طلب محمي لتحويل المُعرِّف المخزّن في الجلسة إلى سجل المستخدم الذي سيراه المُعالِج. اجعله خفيف التكلفة -- فهو يعمل على المسار الساخن.
10.10.2. نموذج تسجيل الدخول، وطلب POST لتسجيل الدخول، وتسجيل الخروج¶
نموذج تسجيل الدخول نفسه عبارة عن صفحة ثابتة تُقدَّم من /sdcard/static/ تمامًا مثل لوحة المعلومات. يرسل النموذج طلب POST إلى /login:
from microdot import redirect
@app.get('/login')
async def login_form(request):
return Response.send_file('/sdcard/static/login.html')
@app.post('/login')
async def do_login(request):
form = request.form
user = USERS.get(form.get('user'))
if not user or not check_password(user, form.get('pass')):
return redirect('/login?error=1')
return await login.login_user(request, user, remember=True)
@app.post('/logout')
async def logout(request):
await login.logout_user(request)
return redirect('/login')
microdot.login.Login.login_user() يكتب ملف تعريف ارتباط الجلسة ويعيد إعادة توجيه 302 إلى أي صفحة حاول العميل الوصول إليها أصلًا (وسيط الاستعلام next=، مع العودة إلى / كقيمة احتياطية). كما يكتب remember=True ملف تعريف ارتباط _remember أطول عمرًا بحيث تبقى الجلسة قائمة بعد إعادة تشغيل المتصفح.
microdot.login.Login.logout_user() يمسح كلا ملفي تعريف الارتباط، ثم تُعيد إعادة توجيه لاحقة المتصفح إلى النموذج.
10.10.3. حماية لوحة المعلومات¶
زخرِف كل مسار يواجه لوحة المعلومات بـ @login بحيث يحصل الطلب غير المُصادَق على إعادة توجيه 302 إلى /login بدلًا من المحتوى:
@app.get('/<path:filename>')
@login
async def static(request, filename):
...
@app.get('/config')
@login
async def get_config(request):
...
@app.post('/config')
@login
async def set_config(request):
...
@app.get('/events')
@login
@with_sse
async def events(request, sse):
...
@app.get('/control')
@login
@with_websocket
async def control(request, ws):
...
تبقى نقاط نهاية API المعتمدة على الرمز (/api/login، /api/ack) معتمدة على الرمز -- فهي مخصصة لتطبيق الهاتف، لا للمتصفح. تتعايش مصادقة الرمز ومصادقة الجلسة بسعادة على نفس app.
10.10.4. الجلسات الحديثة مقابل الجلسات المتذكَّرة¶
يبقي ملف تعريف الارتباط _remember المستخدم مسجّل الدخول عبر عمليات إعادة تشغيل المتصفح، لكنه شكل أضعف من المصادقة -- فقد يكون المتصفح قد تُرك في مقهى. بالنسبة للمسارات التي تغيّر كلمات المرور، أو تسجّل رموز API، أو تفعل أي شيء آخر يستحق إعادة المصادقة، زخرِفها بـ @login.fresh بدلًا من @login. تسجيل الدخول الحديث هو الذي كتب فيه المستخدم كلمة مروره في هذه الجلسة؛ أما تسجيل الدخول المتذكَّر فليس كذلك. لا تحتوي لوحة المعلومات على ما يرقى إلى هذا المستوى، لكن المُزخرِف متاح عندما تحتاج إليه.
تتطلب لوحة المعلومات الآن تسجيل الدخول قبل أن يستجيب أي من مساراتها.