fix: show login flashes and CSRF errors; proxy and cookie options for HTTPS deploys
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -17,3 +17,10 @@
|
||||
|
||||
# 调试:1 开启,勿在生产长期开启
|
||||
# NAV_DEBUG=0
|
||||
|
||||
# 在 Nginx/Caddy 等反向代理后部署时:信任 X-Forwarded-*,以便 Cookie、CSRF 与 HTTPS 判断正确
|
||||
# NAV_TRUST_PROXY=1
|
||||
# 站点仅通过 HTTPS 对外时建议开启(浏览器只带 Secure Cookie)
|
||||
# NAV_SESSION_COOKIE_SECURE=1
|
||||
# CSRF 校验仍失败时,可填前端访问的完整 Origin,多个用英文逗号分隔,例如:
|
||||
# NAV_CSRF_TRUSTED_ORIGINS=https://nav.example.com
|
||||
|
||||
@@ -6,6 +6,7 @@ from typing import Optional
|
||||
from flask import Flask, flash, redirect, render_template, request, url_for
|
||||
from flask_login import LoginManager, current_user, login_required, login_user, logout_user
|
||||
from flask_wtf.csrf import CSRFProtect
|
||||
from werkzeug.middleware.proxy_fix import ProxyFix
|
||||
|
||||
from forms import GroupForm, LoginForm, ServiceForm
|
||||
from models import Service, ServiceGroup, User, db
|
||||
@@ -63,6 +64,16 @@ def create_app() -> Flask:
|
||||
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
|
||||
app.config["WTF_CSRF_TIME_LIMIT"] = None
|
||||
|
||||
if os.environ.get("NAV_SESSION_COOKIE_SECURE") == "1":
|
||||
app.config["SESSION_COOKIE_SECURE"] = True
|
||||
app.config["REMEMBER_COOKIE_SECURE"] = True
|
||||
|
||||
trusted = os.environ.get("NAV_CSRF_TRUSTED_ORIGINS", "").strip()
|
||||
if trusted:
|
||||
app.config["WTF_CSRF_TRUSTED_ORIGINS"] = [
|
||||
o.strip() for o in trusted.split(",") if o.strip()
|
||||
]
|
||||
|
||||
db.init_app(app)
|
||||
login_manager.init_app(app)
|
||||
login_manager.login_view = "login"
|
||||
@@ -271,6 +282,19 @@ def create_app() -> Flask:
|
||||
flash("服务已删除", "success")
|
||||
return redirect(url_for("admin_services"))
|
||||
|
||||
if os.environ.get("NAV_TRUST_PROXY") == "1":
|
||||
app.wsgi_app = ProxyFix(
|
||||
app.wsgi_app, x_for=1, x_proto=1, x_host=1, x_port=1, x_prefix=1
|
||||
)
|
||||
|
||||
if not os.environ.get("NAV_SECRET_KEY"):
|
||||
print(
|
||||
"[nav] 警告: 未设置 NAV_SECRET_KEY。"
|
||||
"若使用 gunicorn/uwsgi 等多 worker,或未固定密钥,登录后会话会失效;"
|
||||
"请在环境变量中配置随机 NAV_SECRET_KEY。",
|
||||
flush=True,
|
||||
)
|
||||
|
||||
return app
|
||||
|
||||
|
||||
|
||||
@@ -7,6 +7,15 @@
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}" />
|
||||
</head>
|
||||
<body>
|
||||
{% with msgs = get_flashed_messages(with_categories=true) %}
|
||||
{% if msgs %}
|
||||
<div class="flash-wrap" role="status">
|
||||
{% for category, message in msgs %}
|
||||
<div class="flash {{ category }}">{{ message }}</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
{% block body %}{% endblock %}
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -6,6 +6,11 @@
|
||||
<h1>本地导航站</h1>
|
||||
<form method="post" novalidate>
|
||||
{{ form.hidden_tag() }}
|
||||
{% if request.method == "POST" and form.csrf_token.errors %}
|
||||
{% for e in form.csrf_token.errors %}
|
||||
<div class="errors" role="alert">{{ e }}</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
<div class="form-row">
|
||||
{{ form.username.label }}
|
||||
{{ form.username(class="") }}
|
||||
|
||||
Reference in New Issue
Block a user