Django session management - user status tracking and security management | Daoman PythonAI

#django session management - user status tracking and security management

📂 Stage: Part 2 - Advanced Features 🎯 Difficulty level: Intermediate ⏰ Estimated study time: 2 hours 🎒 Prerequisite knowledge: 中间件系统

Table of contents


Session foundation and django architecture

HTTP is a stateless protocol, and sessions are the core mechanism that maintains state on the server side and only holds encrypted IDs on the client side.

Minimalist workflow

"""
1. 首次访问 → 生成唯一session_key → 存入Cookie → 服务器创建对应数据存储
2. 后续请求 → 客户端携带session_key → 服务器验证/恢复状态 → 响应时可选更新
3. 到期/登出 → 删除服务器数据 → 清空Cookie
"""

Django session core components

Django implements sessions through three layers: built-in middleware + storage engine + session API:

  1. Middleware layer:django.contrib.sessions.middleware.SessionMiddlewareResponsible for retrieving cookies from requests, retrieving data from storage, and writing cookies in response
  2. Storage layer: Provides 5 official storage solutions
  3. API layer:request.sessionIs a dictionary-like object that can be directly manipulated

Core configuration and storage selection

Production-level security configuration (required)

# settings.py
# Cookie安全三剑客(生产环境强制True)
SESSION_COOKIE_SECURE = True    # 仅HTTPS传输
SESSION_COOKIE_HTTPONLY = True  # 禁止JavaScript访问,防XSS窃取
SESSION_COOKIE_SAMESITE = 'Strict'  # 防CSRF跨站请求伪造

# 过期与更新
SESSION_COOKIE_AGE = 7200       # 2小时超时(秒)
SESSION_EXPIRE_AT_BROWSER_CLOSE = True  # 浏览器关闭即清除敏感会话
SESSION_SAVE_EVERY_REQUEST = False  # 按需更新而非每次(性能优先)

# 开发环境临时覆盖(可选)
if DEBUG:
    SESSION_COOKIE_SECURE = False
    SESSION_COOKIE_SAMESITE = 'Lax'

Comparison and configuration of storage solutions

Storage solutionAdvantagesDisadvantagesApplicable scenarios
DatabasedbStable and durableRead and write dependent databaseSmall and medium-sized applications
CachecacheHigh performancePossible data loss (restart/expiration)Non-core state, high concurrency short session
Cache + databasecached_dbStable performance combination, fallback mechanismSlightly complex configurationPreferred for 90% of production scenarios
FilefileSimple configurationSingle machine, poor performanceOnly local testing
Signature Cookiesigned_cookiesServerless storageSize limit 4KB, client visible (encrypted signature only)Small amount of non-sensitive data

Cache + database configuration example (recommended):

# settings.py (需先配置好CACHES,建议用Redis)
SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db'
SESSION_CACHE_ALIAS = 'default'  # 使用CACHES中配置的别名

# 数据库存储表需先迁移:python manage.py migrate

Common session operations

Basic CRUD

from django.contrib.sessions.backends.db import SessionStore
from django.utils import timezone

def session_demo(request):
    # 1. 设置数据(字典式操作)
    request.session['user_id'] = request.user.id
    request.session['preferences'] = {'theme': 'dark', 'lang': 'zh-CN'}
    request.session.modified = True  # 嵌套修改需手动标记!
    
    # 2. 获取数据
    user_id = request.session.get('user_id', 0)
    theme = request.session.get('preferences', {}).get('theme', 'light')
    
    # 3. 删除数据
    if 'temp_key' in request.session:
        del request.session['temp_key']
    
    # 4. 清空并重新生成会话ID(防会话固定攻击)
    request.session.cycle_key()
    # request.session.flush()  # 完全清除(登出用)
    
    return {'user_id': user_id, 'theme': theme}

Decorator simplifies authorization and session timeout

from functools import wraps
from django.shortcuts import redirect
from django.urls import reverse
from django.contrib import messages
import time

# 要求登录与会话超时检查
def require_auth_with_timeout(timeout_minutes=30):
    def decorator(view_func):
        @wraps(view_func)
        def wrapper(request, *args, **kwargs):
            # 1. 检查登录
            if not request.user.is_authenticated:
                messages.error(request, '请先登录')
                return redirect(f"{reverse('login')}?next={request.get_full_path()}")
            
            # 2. 检查超时
            last_activity = request.session.get('last_activity')
            if last_activity and time.time() - last_activity > timeout_minutes*60:
                request.session.flush()
                messages.error(request, '会话已超时,请重新登录')
                return redirect('login')
            
            # 3. 更新活动时间
            request.session['last_activity'] = time.time()
            return view_func(request, *args, **kwargs)
        return wrapper
    return decorator

# 使用
@require_auth_with_timeout(timeout_minutes=60)
def dashboard(request):
    return {'status': 'ok'}

##Full stack security practice {#Full stack security practice}

1. Prevent session fixation attacks

# 登录/注册成功后必须调用!
def safe_login(request, user):
    from django.contrib.auth import login
    # 保存当前非敏感数据(如果有)
    non_sensitive = {'cart': request.session.get('cart', [])}
    # 重新生成会话ID,清空旧会话
    request.session.cycle_key()
    # 恢复非敏感数据
    request.session.update(non_sensitive)
    # 正常登录
    login(request, user)
    # 添加安全属性
    request.session['user_id'] = user.id
    request.session['original_ip'] = request.META.get('REMOTE_ADDR')
    request.session['original_ua'] = request.META.get('HTTP_USER_AGENT', '')

2. Anti-session hijacking

from django.utils.deprecation import MiddlewareMixin

class SessionHijackProtectionMiddleware(MiddlewareMixin):
    def process_request(self, request):
        if not hasattr(request, 'session') or not request.session.session_key:
            return
        
        # 验证IP和User-Agent是否一致(可选严格/宽松模式)
        original_ip = request.session.get('original_ip')
        original_ua = request.session.get('original_ua')
        current_ip = request.META.get('REMOTE_ADDR')
        current_ua = request.META.get('HTTP_USER_AGENT', '')
        
        # 生产环境建议严格检查,测试环境可放宽(比如IP变化提示再验证)
        if original_ip and original_ua:
            if original_ip != current_ip or original_ua != current_ua:
                import logging
                logging.warning(f"Potential session hijack: {request.session.session_key}")
                request.session.flush()
                return redirect('login')

3. Clean up expired sessions regularly

# 配置系统定时任务(Linux用crontab,Windows用任务计划程序)
# 每天凌晨2点执行:python manage.py clearsessions

# 或者自定义管理命令(扩展功能)
from django.core.management.base import BaseCommand
from django.contrib.sessions.models import Session
from django.utils import timezone

class Command(BaseCommand):
    help = 'Clean up expired sessions and send alert if too many'
    
    def handle(self, *args, **options):
        # 清理30天前的过期会话
        cutoff = timezone.now() - timezone.timedelta(days=30)
        deleted, _ = Session.objects.filter(expire_date__lt=cutoff).delete()
        
        self.stdout.write(self.style.SUCCESS(f"Successfully deleted {deleted} expired sessions"))
        
        # 异常提醒
        if deleted > 10000:
            import logging
            logging.error(f"Unusually high expired sessions: {deleted}")

Quick solution to common problems

1. Nested dictionary modification does not take effect

# ❌ 错误:request.session['preferences']['theme'] = 'dark'
# ✅ 正确:先获取、修改、再保存,或手动标记
preferences = request.session.get('preferences', {})
preferences['theme'] = 'dark'
request.session['preferences'] = preferences  # 自动标记
# 或者嵌套修改后加:request.session.modified = True

2. Cross-domain session does not take effect

# 1. 安装django-cors-headers
# pip install django-cors-headers

# 2. settings.py配置
INSTALLED_APPS = ['corsheaders', ...]
MIDDLEWARE = ['corsheaders.middleware.CorsMiddleware', ...]  # 必须放在SessionMiddleware之前!

# 3. 允许的域名和凭证
CORS_ALLOWED_ORIGINS = ['https://your-frontend.com', 'http://localhost:5173']
CORS_ALLOW_CREDENTIALS = True  # 必须True才能带Cookie

3. Session data is too large

# 不要在会话中存大对象(比如查询集、图片二进制)
# ✅ 存ID或轻量数据,需要时再查数据库
# ❌ request.session['products'] = Product.objects.all()
# ✅ request.session['recent_product_ids'] = [1,2,3,4,5]

Summary of this chapter

💡 Core Points: Session management needs to balance security, performance, user experience

  1. Basic Configuration Must Do: Three Musketeers of Security + Reasonable Expiration Time
  2. Storage Priority:cached_dbAdapt to most scenarios
  3. Key operations should be noted: nested modification tags, login/logout cycle_key/flush
  4. Security cannot be ignored: fixed attack protection, hijacking detection, regular cleaning
  5. Quick troubleshooting: Nested modifications, cross-domain configurations, and data size are frequent pitfalls.

🔗 Recommended related tutorials