我正在 Replit 中运行下面的 Flask 应用程序。当我在 MS Edge 或 Chrome 中运行它时,会话在重定向()后重置。当我在 Firefox 中运行它时它可以工作。这是代码:
from flask import Flask, render_template, request, redirect, url_for, session
import secrets
app = Flask(__name__)
app.secret_key = secrets.token_hex(16)
users = {
'user1': {'username': 'user1', 'password': 'password1'},
'user2': {'username': 'user2', 'password': 'password2'}
}
@app.route('/')
def home():
print("loading home", session)
return render_template('index.html')
@app.route('/signup', methods=['GET', 'POST'])
def signup():
if request.method == 'POST':
username = request.form['username']
password = request.form['password']
users[username] = {'username': username, 'password': password}
return redirect(url_for('home'))
return render_template('signup.html')
@app.route('/login', methods=['GET', 'POST'])
def login():
print("loading login", session)
if request.method == 'POST':
username = request.form['username']
password = request.form['password']
user = users.get(username)
if user and user['password'] == password:
session['username'] = username
print("session set", session)
return redirect(url_for('home'))
return render_template('login.html')
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8080, debug=True)
这是控制台输出:
172.31.196.1 - - [21/Dec/2023 23:03:48] "OPTIONS * HTTP/1.1" 404 -
loading home <SecureCookieSession {}>
172.31.196.1 - - [21/Dec/2023 23:03:48] "GET / HTTP/1.1" 200 -
loading login <SecureCookieSession {}>
172.31.196.1 - - [21/Dec/2023 23:03:51] "GET /login HTTP/1.1" 200 -
loading login <SecureCookieSession {}>
172.31.196.1 - - [21/Dec/2023 23:03:51] "GET /login HTTP/1.1" 200 -
loading login <SecureCookieSession {}>
session set <SecureCookieSession {'username': 'user1'}>
172.31.196.1 - - [21/Dec/2023 23:04:06] "POST /login HTTP/1.1" 302 -
loading home <SecureCookieSession {}>
172.31.196.1 - - [21/Dec/2023 23:04:06] "GET / HTTP/1.1" 200 -
loading home <SecureCookieSession {}>
172.31.196.1 - - [21/Dec/2023 23:04:06] "GET / HTTP/1.1" 200 -
我已验证 cookie 已启用。
我已经更新了您的代码,以便在 Flask 中提供功能性的用户注册流程。当前代码使用字典中的
users
,我假设您将使用一些数据库来存储用户信息。
我添加了以下内容:
username
中设置 session
。flash
中设置必要的app.py
消息并在模板中显示它们。app.py
:
from flask import Flask, render_template, request, redirect, url_for, session, flash
import secrets
app = Flask(__name__)
app.secret_key = secrets.token_hex(16)
users = {
'user1': {'username': 'user1', 'password': 'password1'},
'user2': {'username': 'user2', 'password': 'password2'}
}
@app.route('/')
def home():
username = session.get("username")
return render_template('index.html', username=username)
@app.route('/logout')
def logout():
session.pop('username', None)
flash('You have been logged out.', 'info')
return redirect(url_for('login'))
@app.route('/signup', methods=['GET', 'POST'])
def signup():
if request.method == 'POST':
username = request.form['username']
password = request.form['password']
if username not in users:
flash('Account created successfully.', 'success')
users[username] = {'username': username, 'password': password}
session['username'] = username
return redirect(url_for('home'))
else:
flash('Username already exists. Choose a different one.', 'error')
print(f"Username {username} exists")
return redirect(url_for('signup'))
return render_template('signup.html')
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
username = request.form['username']
password = request.form['password']
user = users.get(username)
if user and user['password'] == password:
session['username'] = username
flash('Logged in successfully.', 'success')
return redirect(url_for('home'))
else:
flash('User not found. Try again.', 'error')
return render_template('login.html')
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8080, debug=True)
index.html
:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Home</title>
<style>
.error {
color: red;
}
.success {
color: green;
}
</style>
</head>
<body>
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
<ul>
{% for category, message in messages %}
<li class="{{ category }}">{{ message }}</li>
{% endfor %}
</ul>
{% endif %}
{% endwith %}
{% if username %}
<p>Welcome, {{ username }}!</p>
<a href="{{ url_for('logout') }}">Logout</a>
{% else %}
<p>Not logged in. Please sign up or log in:</p>
<a href="{{ url_for('signup') }}">Sign Up</a> | <a href="{{ url_for('login') }}">Login</a>
{% endif %}
</body>
</html>
signup.html
:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Sign Up</title>
<style>
.error {
color: red;
}
.success {
color: green;
}
</style>
</head>
<body>
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
<ul>
{% for category, message in messages %}
<li class="{{ category }}">{{ message }}</li>
{% endfor %}
</ul>
{% endif %}
{% endwith %}
<h2>Sign Up</h2>
<form method="post" action="{{ url_for('signup') }}">
<label for="username">Username:</label>
<input type="text" name="username" required><br>
<label for="password">Password:</label>
<input type="password" name="password" required><br>
<input type="submit" value="Sign Up">
</form>
<p>Already have an account?
<a href="{{ url_for('login') }}">Login</a>
</p>
</body>
</html>
login.html
:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Login</title>
<style>
.error {
color: red;
}
.success {
color: green;
}
</style>
</head>
<body>
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
<ul>
{% for category, message in messages %}
<li class="{{ category }}">{{ message }}</li>
{% endfor %}
</ul>
{% endif %}
{% endwith %}
<h2>Login</h2>
<form method="post" action="{{ url_for('login') }}">
<label for="username">Username:</label>
<input type="text" name="username" required><br>
<label for="password">Password:</label>
<input type="password" name="password" required><br>
<input type="submit" value="Login">
</form>
<p>No account?
<a href="{{ url_for('signup') }}">Sign Up</a>
</p>
</body>
</html>
解决了。问题在于基于 Chromium 的浏览器如何实现 RFC 2109(感谢 @davidism 的解释)。修复方法是使用以下代码设置 Set-Cookie 标头的 SameSite 和 Secure 属性:
app.config['SESSION_COOKIE_SAMESITE'] = 'None'
app.config['SESSION_COOKIE_SECURE'] = True