Skip to content

Flask - Sessions

HTTP 是一种无状态协议(stateless protocol),意味着客户端向服务器发出的每个请求都是独立处理的。服务器本身不保留与同一客户端先前交互的记忆。为了在多个请求之间维护特定用户的信息(例如,保持用户登录状态、存储购物车内容),我们使用会话(sessions)。

与直接将数据存储在用户浏览器中的 Cookie 不同,Flask 默认的会话实现使用的是签名 Cookie(signed cookies)。这意味着会话数据本身(如用户名或用户 ID)存储在发送到客户端浏览器的 Cookie 中,但它是使用秘密密钥(secret key)由服务器进行加密签名的。这个签名允许服务器验证会话数据是否已被客户端篡改。

由于数据存储在客户端(在签名 Cookie 中),这种方法避免了为简单的会话数据进行服务器端存储的需求,使其具有可扩展性。然而,这也意味着你不应该直接在会话中存储大量数据或高度敏感的信息,因为 Cookie 有大小限制,并且数据(尽管已签名)对客户端仍然是可见的。

要在 Flask 中使用会话 (sessions),你需要:

  • 设置 SECRET_KEY: 这对于安全地签署会话 Cookie 来说绝对至关重要。它应该是一个长、随机且不可预测的字符串,并且必须保密。在生产环境中,应从环境变量或配置管理系统加载它,切勿直接硬编码在源代码中。
  • 导入并使用 session 对象: Flask 通过从 flask 导入的 session 对象提供会话功能。这个对象行为类似于 Python 字典。

设置会话变量很简单:

from flask import session
# 示例:用户成功登录后存储用户名
session['username'] = 'admin'

读取会话变量:

# 检查用户是否已登录
if 'username' in session:
username = session['username']
print(f"User {username} is logged in.")
else:
print("User is not logged in.")

移除会话变量(例如,在注销时):

# 从会话中移除 'username',如果不存在则返回 None
session.pop('username', None)

我们来看一个简单的登录/注销示例:

from flask import Flask, session, redirect, url_for, request, render_template_string
app = Flask(__name__)
# IMPORTANT: Set a strong, random secret key!
# In a real app, load this from config/environment variable.
app.config['SECRET_KEY'] = 'your-very-secure-and-random-secret-key'
# Simple in-memory user store for demonstration
users = {'admin': 'password123'}
@app.route('/')
def index():
if 'username' in session:
username = session['username']
return f'''
Logged in as **{username}**<br>
<a href="{url_for('logout')}">Click here to log out</a>
'''
return f'''
You are not logged in.<br>
<a href="{url_for('login')}">Click here to log in</a>
'''
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
submitted_user = request.form.get('username')
submitted_pass = request.form.get('password')
# Basic validation (replace with proper password hashing in real apps!)
if submitted_user in users and users[submitted_user] == submitted_pass:
session['username'] = submitted_user
return redirect(url_for('index'))
else:
# Invalid credentials, show form again with error (optional)
error = 'Invalid username or password'
return render_template_string(LOGIN_FORM, error=error)
# For GET request, just show the login form
return render_template_string(LOGIN_FORM, error=None)
# Define the login form as a multi-line string constant
LOGIN_FORM = """
<!doctype html>
<title>Login</title>
<h1>Login</h1>
{% if error %}<p style="color:red;">{{ error }}</p>{% endif %}
<form method="post">
<p>Username: <input type=text name=username></p>
<p>Password: <input type=password name=password></p>
<p><input type=submit value=Login></p>
</form>
"""
@app.route('/logout')
def logout():
# Remove the username from the session if it's there
session.pop('username', None)
return redirect(url_for('index'))
if __name__ == '__main__':
app.run(debug=True)

此示例的工作原理:

  • / (index): 检查 session 中是否存在 username。如果存在,则显示欢迎消息和注销链接。如果不存在,则显示登录链接。
  • /login (GET): 显示一个简单的 HTML 登录表单(为简化起见,在 LOGIN_FORM 字符串中定义并使用 render_template_string 渲染;通常你会使用单独的模板文件)。
  • /login (POST): 从提交的表单 (request.form) 中获取用户名和密码。它对 users 字典执行非常基础的检查(在实际应用中,你必须安全地哈希密码)。如果凭据有效,它将 username 存储在 session 中并重定向到首页 (/)。如果无效,它会重新显示带有错误的表单。
  • /logout: 使用 session.pop() 从 session 中移除 username 并重定向到首页。
  • SECRET_KEY: 使 Flask 能够安全地签署会话 Cookie。

运行此应用程序(python app.py)并访问 http://localhost:5000/。你会看到“未登录”的消息。点击登录链接,输入“admin”和“password123”,然后提交。你将被重定向回 /,现在显示“以 admin 身份登录”的消息。点击注销链接将清除会话,使你返回“未登录”状态。

替代会话接口: 尽管 Flask 默认的签名 Cookie 会话很方便,但它并非适用于所有情况(例如,存储大量数据、需要服务器端失效)。Flask-Session 等 Flask 扩展允许你配置不同的会话后端,如 Redis、Memcached、SQLAlchemy(数据库)或文件系统,这些后端将会话数据存储在服务器端,而只向客户端发送一个会话 ID Cookie。