Django中如何在访问实际视图页面前先展示主密码验证页面
实现方案
核心逻辑是通过装饰器拦截受保护的请求,结合session记录主密码校验状态,校验通过后自动跳转回原目标地址,具体实现步骤如下:
步骤1:编写主密码校验装饰器
所有需要校验主密码的视图都可以复用这个装饰器,逻辑是先检查session中是否有未过期的校验通过标记,没有的话就跳转到密码校验页,同时携带当前请求地址作为跳转参数:
from django.shortcuts import redirect from functools import wraps from datetime import datetime, timedelta def require_master_password(view_func): @wraps(view_func) def _wrapped_view(request, *args, **kwargs): # 读取session中的校验状态和过期时间 verified = request.session.get('master_password_verified', False) expire_ts = request.session.get('master_password_expire', 0) current_ts = datetime.now().timestamp() # 校验未通过或已过期,跳转到校验页 if not verified or current_ts > expire_ts: return redirect(f"/check-master-password/?next={request.path}") return view_func(request, *args, **kwargs) return _wrapped_view
步骤2:修改密码校验视图
调整后的视图会接收跳转目标地址,校验通过后将状态写入session,再跳回原目标页面:
def check_master_password_view(request): page = 'check' profile = request.user.profile # 读取跳转目标,默认跳转到用户首页 next_url = request.GET.get('next', '/') if request.method == "POST": # 优先从POST参数中拿跳转地址(从表单隐藏字段传递) next_url = request.POST.get('next', next_url) checked = check_master_password(request.POST['password'], profile.master_password) if checked: # 校验通过,写入session,默认设置10分钟有效期,可自行调整 request.session['master_password_verified'] = True request.session['master_password_expire'] = (datetime.now() + timedelta(minutes=10)).timestamp() return redirect(next_url) else: messages.error(request, 'You entered wrong password !') context = { 'page': page, 'next_url': next_url } return render(request, 'users/create_or_check_master_password.html', context)
步骤3:修改密码校验模板
在create_or_check_master_password.html的表单中添加隐藏字段,传递跳转目标地址:
<form method="POST"> {% csrf_token %} <!-- 新增隐藏字段传递next参数 --> <input type="hidden" name="next" value="{{ next_url }}"> <!-- 原有密码输入等表单内容 --> </form>
步骤4:给受保护的视图添加装饰器
比如你的日记详情、修改、删除视图,直接加装饰器即可:
@require_master_password def detail_diary_view(request, pk): profile = request.user.profile diary = profile.diary_set.get(id=pk) diary.diary = decrypt_diary(request, diary.diary) context = { 'diary': diary } return render(request, 'diary/detail_diary.html', context)
额外优化建议
- 用户登出时记得清空session中的校验标记,避免共用设备时的安全风险,可以在登出逻辑中加入:
request.session.pop('master_password_verified', None) request.session.pop('master_password_expire', None) - 如果使用类视图,用
method_decorator把装饰器绑定到dispatch方法即可生效。
内容的提问来源于stack exchange,提问作者A.Emre SAVAŞ
相关产品推荐
相关产品推荐

