欢迎光临
我们一直在努力

小白也能懂的Session:服务器如何“记住”你

诸神缄默不语-个人技术博文与视频目录

我的理解:session是用户在通过客户端与服务器交互的过程中,服务器如何来认证某一个指定用户的一套认证数据。

文章目录

  • 一句话解释Session
  • 为什么需要Session?
  • Session的工作原理:一个咖啡店的例子
  • 用Python理解Session
  • 实际Web应用中的Session
  • Session的存储位置
    • A. 客户端Session存储(如Flask默认)
    • B. 服务器端Session存储(传统方式)
      • Session的实现方式
        • 1. Cookie-based Session(最常见)
        • 2. Token-based Session
        • 3. URL重写
      • 实际应用建议
  • Session vs Cookie:容易混淆的兄弟
  • Session的优缺点
    • ✅ **优点:**
    • ❌ **缺点:**
  • Session的安全问题
    • 1. **Session劫持**
    • 2. **Session固定攻击**
    • 3. **跨站请求伪造(CSRF)**
  • 实际项目中的最佳实践
  • 思考题
  • 总结

一句话解释Session

Session是服务器用来“记住”你是谁的一种方式。就像你去健身房办卡,前台给你一个手牌(Session ID),你把手牌交给教练,教练就知道你是哪个会员,不用每次都查身份证。

为什么需要Session?

想象一下这个场景:你在网上购物,把商品加入购物车,然后继续浏览。服务器怎么知道“刚才把商品加入购物车的人”和“现在正在浏览的人”是同一个人呢?

这就是HTTP协议的一个特点:HTTP是无状态的。每次请求都是独立的,服务器默认不会记住之前的请求。

Session就是为了解决这个问题而生的!

Session的工作原理:一个咖啡店的例子

假设有一家特别的咖啡店:

  • 第一次光顾:店员不认识你,但给你一张会员卡(Session ID)

  • 点咖啡:你把会员卡给店员,店员在后台系统查到你的信息(偏好、余额等)

  • 续杯:再次出示会员卡,店员就知道你刚才点了什么

  • 离开:一段时间没来,会员卡自动失效(Session过期)

  • Session的工作方式完全一样!

    用Python理解Session

    让我们通过代码看看Session在实际中如何工作。首先创建一个简单的Web应用:

    import uuid
    import datetime

    # 模拟用户数据库(实际中应该用真正的数据库)
    users_db = {
    "alice": {"password": "alice123", "name": "Alice Smith", "vip_level": 3},
    "bob": {"password": "bob456", "name": "Bob Johnson", "vip_level": 1}
    }

    # 模拟Session存储(实际中Flask等Web框架会处理,这里展示原理)
    # 格式:{session_id: {用户数据}}
    sessions_storage = {}

    def create_session(user_id):
    """创建新的Session"""
    session_id = str(uuid.uuid4()) # 生成唯一ID
    sessions_storage[session_id] = {
    'user_id': user_id,
    'login_time': datetime.datetime.now(),
    'last_active': datetime.datetime.now(),
    'cart': [] # 购物车
    }
    return session_id

    def get_session(session_id):
    """获取Session信息"""
    if session_id in sessions_storage:
    # 更新最后活动时间
    sessions_storage[session_id]['last_active'] = datetime.datetime.now()
    return sessions_storage[session_id]
    return None

    def cleanup_expired_sessions():
    """清理过期的Session(实际中会有自动清理机制)"""
    expired = []
    for session_id, data in sessions_storage.items():
    # 假设30分钟不活动就过期
    inactive_time = datetime.datetime.now() data['last_active']
    if inactive_time.total_seconds() > 1800: # 1800秒=30分钟
    expired.append(session_id)

    for session_id in expired:
    del sessions_storage[session_id]
    print(f"清理了 {len(expired)} 个过期Session")

    # 手动模拟Session流程(不使用Flask内置的Session)
    print("=== 手动模拟Session流程 ===")

    # 用户登录
    print("1. 用户Alice登录…")
    session_id = create_session("alice")
    print(f" 服务器创建Session,ID: {session_id[:8]}…")
    print(f" 服务器存储的内容: {sessions_storage}")
    print("-" * 40)

    # 用户访问其他页面
    print("2. Alice浏览商品,加入购物车…")
    alice_session = get_session(session_id)
    if alice_session:
    alice_session['cart'].append("咖啡豆")
    alice_session['cart'].append("咖啡杯")
    print(f" 服务器查到Session: {alice_session}")
    print("-" * 40)

    # 另一个用户登录
    print("3. 用户Bob登录…")
    bob_session_id = create_session("bob")
    bob_session = get_session(bob_session_id)
    print(f" Bob的Session ID: {bob_session_id[:8]}…")
    print(f" 服务器现在有 {len(sessions_storage)} 个活跃Session")
    print("-" * 40)

    # 清理过期Session
    print("4. 清理过期Session…")
    cleanup_expired_sessions()
    print("-" * 40)

    实际Web应用中的Session

    在默认情况下,Flask使用客户端会话(client-side session),将会话数据存储在客户端的cookie中,并且通过密钥进行签名,因此服务器重启后,只要密钥不变,会话数据仍然有效(因为数据存储在客户端,服务器只是验证签名并读取数据)。

    (上面一节的例子里我们手动创建的session放在了服务端,那不一样)

    因为Flask默认使用了secure cookie来存储Session数据。 在传统的Web应用中(比如PHP、Java Servlet等),Session数据是存储在服务器端的,而客户端只存储一个Session ID。所以需要注意:

  • 不要存储敏感信息:因为数据在客户端,虽然签名防止了篡改,但数据本身是可以被用户看到的(除非你使用加密,Flask默认只签名不加密)。

  • 数据大小限制:Cookie的大小有限制(通常为4KB),所以不能存储大量数据。

  • 使用flask-session扩展可以轻松地将Session存储到服务器端。这样,客户端只存储一个Session ID,而Session数据则存储在服务器端的Redis、数据库或文件系统中。

    现在让我们看看在真实的Web应用中如何使用Session:

    # pip install flask

    import datetime
    from flask import Flask, session, request, render_template_string

    app = Flask(__name__)
    app.secret_key = 'your-secret-key-here'

    # HTML模板(简化版)
    login_page = """
    <!DOCTYPE html>
    <html>
    <body>
    {% if session.get('user_id') %}
    <h2>欢迎回来,{{ session.get('username') }}!</h2>
    <p>你的购物车中有 {{ session.get('cart')|length }} 件商品</p>
    <a href="/logout">退出登录</a>
    {% else %}
    <form action="/login" method="post">
    用户名:<input name="username"><br>
    密码:<input type="password" name="password"><br>
    <button type="submit">登录</button>
    </form>
    {% endif %}
    </body>
    </html>
    """

    # 路由定义
    @app.route('/')
    def home():
    """首页"""
    return render_template_string(login_page)

    @app.route('/login', methods=['POST'])
    def login():
    """登录处理"""
    username = request.form.get('username')
    password = request.form.get('password')

    # 简单验证(实际中应该查询数据库)
    if username == "alice" and password == "alice123":
    # 在Session中存储用户信息
    session['user_id'] = 1
    session['username'] = username
    session['cart'] = [] # 初始化购物车
    session['login_time'] = datetime.datetime.now().isoformat()
    return "登录成功!<a href='/'>返回首页</a>"
    else:
    return "用户名或密码错误"

    @app.route('/add_to_cart')
    def add_to_cart():
    """添加商品到购物车"""
    if 'user_id' not in session:
    return "请先登录"

    product = request.args.get('product', '未知商品')
    if 'cart' not in session:
    session['cart'] = []

    session['cart'].append(product)
    session.modified = True # 告诉Flask Session已修改

    return f"已添加 {product} 到购物车。购物车现在有 {len(session['cart'])} 件商品。<br><a href='/cart'>查看购物车</a>"

    @app.route('/cart')
    def view_cart():
    """查看购物车"""
    if 'user_id' not in session:
    return "请先登录"

    cart_items = session.get('cart', [])
    return f"你的购物车:<br>" + "<br>".join(cart_items) + f"<br><br>共 {len(cart_items)} 件商品"

    @app.route('/logout')
    def logout():
    """退出登录"""
    session.clear() # 清除Session
    return "已退出登录。<a href='/'>返回首页</a>"

    if __name__ == '__main__':
    app.run(debug=True)

  • 运行脚本后flask服务在5000端口自动挂起,访问http://127.0.0.1:5000可以看到登录表单 (如果当前5000端口被占用,端口号会自动顺移,在终端日志中可以看到实际端口号)

  • 输入用户名alice,密码alice123,即可显示登录成功!<a href='/'>返回首页</a>

  • 访问http://127.0.0.1:5000/add_to_cart?product=咖啡即可添加商品

  • 现在可以进入http://127.0.0.1:5000/cart访问购物车,也可以进入http://127.0.0.1:5000/访问首页,都可以看到购物车中有一件商品这项信息

  • 在上一节我们模拟的session是存在服务端内存中的,所以服务器重启session就会失效。

    实际中常用的策略是session永久化储存在服务器中,如存储在Redis中:

    # 使用服务器端Session的示例(需要额外安装redis和flask-session扩展)
    from flask import Flask, session
    from flask_session import Session
    import redis

    app = Flask(__name__)
    app.config['SECRET_KEY'] = 'your-secret-key-here'

    # 配置服务器端Session(Redis)
    app.config['SESSION_TYPE'] = 'redis' # 存储类型
    app.config['SESSION_REDIS'] = redis.from_url('redis://localhost:6379')
    app.config['SESSION_PERMANENT'] = False # 浏览器关闭后Session过期
    app.config['SESSION_USE_SIGNER'] = True # 对Session ID签名
    app.config['SESSION_KEY_PREFIX'] = 'myapp:' # Redis键前缀

    # 初始化Session扩展
    Session(app)

    Session的存储位置

    A. 客户端Session存储(如Flask默认)

    • 数据存在客户端Cookie中

    • 服务器只负责验证签名

    • 适合:小型应用,非敏感数据

    客户端: [签名Cookie: encoded_data.signature]
    服务器: [验证签名,解码数据]
    关系: 服务器不存储,但每次处理整个数据

    B. 服务器端Session存储(传统方式)

    • 客户端只存session ID

    • 内存存储:快速,重启丢失

    • 数据库存储:持久,可共享,较慢

    • Redis/Memcached存储:快速,持久,适合分布式

    • 适合:大型应用,敏感数据

    Session的实现方式

    1. Cookie-based Session(最常见)
    • 服务器生成Session ID,通过Cookie发送给浏览器

    • 浏览器每次请求自动带上这个Cookie

    • 服务器通过Session ID查找对应的Session数据

    客户端: [Session ID: abc123]
    服务器: [abc123 → {用户数据}]
    关系: 1对1映射

    2. Token-based Session
    • 如JWT,所有信息都存在token里

    • 不需要服务器存储Session数据

    客户端: [JWT: header.payload.signature]
    服务器: [验证签名,解码payload]
    关系: 自包含令牌

    3. URL重写
    • Session ID附加在URL后面(不安全,现在很少用)

    实际应用建议

    # 现代Web应用常见做法:混合使用

    # 1. 短期Session用于敏感操作(如管理后台)
    # 使用服务器端Session,可以随时撤销

    # 2. JWT用于API和移动端
    # 无状态,适合分布式系统

    # 3. 刷新令牌机制
    # 短期访问令牌 + 长期刷新令牌

    class HybridAuthSystem:
    def __init__(self):
    # 敏感操作用服务器端Session
    self.sensitive_sessions = {}

    # API访问用JWT
    self.jwt_secret = "api_secret_key"

    def web_login(self, user_id, sensitive=False):
    """Web登录,可选择使用服务器端Session"""
    if sensitive:
    # 敏感操作:使用服务器端Session
    session_id = self._create_server_session(user_id)
    return {'type': 'session', 'session_id': session_id}
    else:
    # 普通操作:使用JWT
    token = self._create_jwt_token(user_id)
    return {'type': 'jwt', 'token': token}

    def api_login(self, user_id):
    """API登录:总是用JWT"""
    token = self._create_jwt_token(user_id)
    refresh_token = self._create_refresh_token(user_id)
    return {
    'access_token': token,
    'refresh_token': refresh_token,
    'expires_in': 3600
    }

    Session vs Cookie:容易混淆的兄弟

    很多人分不清Session和Cookie,其实很简单:

    特性CookieSession
    存储位置 浏览器 服务器
    安全性 较低(用户可看到) 较高(存在服务器)
    容量限制 4KB左右 通常更大
    过期时间 可设置 可设置
    工作方式 每次请求自动发送 通过Session ID关联

    关键关系:Session通常依赖Cookie来传递Session ID!

    Session的优缺点

    ✅ 优点:

  • 相对安全:敏感数据存在服务器

  • 存储容量大:可以存更多信息

  • 灵活性高:可以存复杂对象

  • ❌ 缺点:

  • 服务器压力:大量用户时占用内存

  • 扩展困难:多台服务器需要同步Session

  • CSRF攻击:需要额外防护

  • Session的安全问题

    1. Session劫持

    • 攻击者窃取Session ID,冒充用户

    • 防护:使用HTTPS、定期更换Session ID

    2. Session固定攻击

    • 攻击者让用户使用已知的Session ID

    • 防护:登录成功后生成新的Session ID

    3. 跨站请求伪造(CSRF)

    • 诱导用户执行非本意的操作

    • 防护:使用CSRF Token

    实际项目中的最佳实践

    # Flask Session配置示例
    app.config.update(
    SECRET_KEY='development-key-change-in-production',
    SESSION_COOKIE_NAME='myapp_session', # Cookie名称
    SESSION_COOKIE_HTTPONLY=True, # 防止XSS攻击读取Cookie
    SESSION_COOKIE_SECURE=True, # 仅HTTPS传输(生产环境)
    SESSION_COOKIE_SAMESITE='Lax', # 防止CSRF
    PERMANENT_SESSION_LIFETIME=datetime.timedelta(hours=2), # 过期时间
    SESSION_TYPE='redis', # 使用Redis存储Session(需要flask-redis)
    SESSION_USE_SIGNER=True # 签名Session ID防止篡改
    )

    思考题

  • 你在淘宝添加商品到购物车,然后关掉浏览器,第二天打开,购物车还在。这是如何实现的?

    • 答案:可能使用了持久化的Session(比如存到数据库),或者把购物车数据存到了浏览器的LocalStorage。
  • 银行网站通常15分钟不操作就自动退出,这是为什么?

    • 答案:为了安全,设置了Session的短过期时间。
  • 为什么有些网站登录时有个"记住我"的选项?

    • 答案:勾选后Session/Cookie的过期时间更长(比如30天)。
  • 总结

    Session就像是服务器给用户的临时身份证:

    • Session ID是身份证号码

    • Session数据是身份证上的信息

    • 过期时间是身份证的有效期

    理解了Session,你就能明白:

  • 为什么登录后网站知道你是谁

  • 为什么购物车里的商品不会消失

  • 为什么银行网站会自动退出


  • 提示:本文示例代码主要用于教学演示,实际生产环境需要考虑更多安全因素和性能优化。建议使用Web框架内置的Session机制,而不是自己实现。

    赞(0)
    未经允许不得转载:171主机测评 » 小白也能懂的Session:服务器如何“记住”你
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址