如何在Django中为不同登录用户设置差异化会话过期时间
Django 实现不同用户自定义单日会话时长方案
以下是完整实现步骤,覆盖从数据存储到逻辑校验的全流程:
1. 新增用户使用时长配置模型
首先扩展用户关联模型,存储管理员配置的时长、使用记录、锁定状态等数据:
from django.db import models from django.contrib.auth import get_user_model from django.db.models.signals import post_save from django.dispatch import receiver User = get_user_model() class UserUsageProfile(models.Model): user = models.OneToOneField(User, on_delete=models.CASCADE, related_name="usage_profile") daily_allowed_hours = models.IntegerField(default=0, help_text="单日允许使用时长,单位:小时,0代表不限制") today_used_seconds = models.IntegerField(default=0, help_text="当日已使用秒数") last_active_date = models.DateField(null=True, blank=True, help_text="上次活跃日期") last_request_time = models.DateTimeField(null=True, blank=True, help_text="上次请求时间") is_locked = models.BooleanField(default=False, help_text="时长耗尽是否锁定") # 自动为新创建的用户生成使用配置 @receiver(post_save, sender=User) def create_usage_profile(sender, instance, created, **kwargs): if created: UserUsageProfile.objects.create(user=instance)
2. 基础配置调整
修改settings.py添加必要配置:
MIDDLEWARE = [ # ... 其他默认中间件 'django.contrib.sessions.middleware.SessionMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', # 自定义时长校验中间件,必须放在Session和认证中间件之后 'yourapp.middlewares.UserUsageLimitMiddleware', ] # 每次请求都更新会话,确保活跃状态实时同步 SESSION_SAVE_EVERY_REQUEST = True
3. 核心校验中间件实现
通过中间件拦截所有用户请求,实时计算使用时长、判断过期逻辑:
import datetime from django.shortcuts import redirect from django.contrib.auth import logout from django.urls import reverse class UserUsageLimitMiddleware: def __init__(self, get_response): self.get_response = get_response # 无需校验的路径白名单,避免死循环 self.exempt_paths = [ reverse('login'), reverse('admin:index'), '/static/', '/favicon.ico' ] def __call__(self, request): # 跳过白名单路径 for path in self.exempt_paths: if request.path.startswith(path): return self.get_response(request) # 仅处理已登录的普通用户,管理员可按需设置是否受限制 if request.user.is_authenticated and not request.user.is_staff: profile = request.user.usage_profile now = datetime.datetime.now() today = now.date() # 已锁定用户直接强制登出 if profile.is_locked: logout(request) return redirect(f"{reverse('login')}?error=账号已锁定,请联系管理员授权") # 新日期重置当日使用时长 if profile.last_active_date != today: profile.today_used_seconds = 0 profile.last_active_date = today # 累计两次请求间隔的使用时长 if profile.last_request_time: time_delta = now - profile.last_request_time profile.today_used_seconds += int(time_delta.total_seconds()) # 时长耗尽锁定账号并强制登出 if profile.daily_allowed_hours > 0 and profile.today_used_seconds >= profile.daily_allowed_hours * 3600: profile.is_locked = True profile.save() logout(request) return redirect(f"{reverse('login')}?error=今日使用时长已耗尽,请联系管理员重新授权") # 更新最新请求时间 profile.last_request_time = now profile.save() return self.get_response(request)
4. 登录环节增加锁定校验
避免用户时长耗尽后重新登录,自定义登录表单增加校验逻辑:
from django.contrib.auth.forms import AuthenticationForm from django import forms class CustomLoginForm(AuthenticationForm): def confirm_login_allowed(self, user): super().confirm_login_allowed(user) if not user.is_staff and user.usage_profile.is_locked: raise forms.ValidationError("您的账号已被锁定,请联系管理员重新授权")
将自定义表单配置到登录视图即可生效。
5. 管理员后台配置入口
将时长配置模型注册到Django Admin,方便管理员调整用户时长、解锁账号:
from django.contrib import admin from .models import UserUsageProfile @admin.register(UserUsageProfile) class UserUsageProfileAdmin(admin.ModelAdmin): list_display = ['user', 'daily_allowed_hours', 'today_used_seconds', 'is_locked', 'last_active_date'] list_filter = ['is_locked', 'last_active_date'] search_fields = ['user__username', 'user__email'] # 允许管理员编辑的字段 fields = ['user', 'daily_allowed_hours', 'today_used_seconds', 'is_locked']
注意事项
- 分布式部署场景下建议将会话存储到Redis等统一缓存服务,避免多实例会话不同步
- 可根据需求扩展剩余时长告警逻辑,比如剩余10%时长时发送站内信提醒用户
- 如果需要精确到秒级校验,可以将中间件的时间差计算逻辑调整为更细的粒度
内容的提问来源于stack exchange,提问作者Dhatri Kapuriya
相关产品推荐
相关产品推荐

