You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django中如何通过邮件访问码链接实现跨设备浏览器的用户会话保持

Django跨设备/浏览器会话保持实现方案

你提到的加密request ID方案是可行的,另外还有更简便的基于Django内置签名的无状态方案,无需额外开发API,两种方案的实现细节如下:

方案1:内置签名无状态实现(更优,无需额外存储)

该方案直接用Django自带的signing模块对用户验证信息签名,签名后的字符串直接放在跳转链接参数中,后端直接校验签名有效性即可,无需查库/查缓存,性能更高。

实现步骤

  • 生成带签名的跳转链接
    用户提交信息验证通过后,把需要保留的用户标识、验证状态、有效期等信息打包成字典,签名后拼接为跳转链接参数,随邮件发送
    import time
    from django.core import signing
    from django.core.mail import send_mail
    
    # 构造需要保留的用户验证信息
    payload = {
        "user_id": 123, # 替换为实际的用户唯一标识
        "verify_passed": True,
        "expire_at": int(time.time()) + 3600 * 24 # 签名24小时内有效
    }
    # 生成防篡改的签名字符串
    signed_payload = signing.dumps(payload)
    # 拼接跳转链接
    redirect_url = f"https://你的域名.com/verify/callback?signed={signed_payload}"
    # 发送邮件逻辑
    send_mail(
        subject="验证通过通知",
        message=f"点击链接继续操作:{redirect_url}",
        from_email="admin@你的域名.com",
        recipient_list=[用户邮箱],
    )
    
  • 回调页校验签名完成会话创建
    编写回调视图处理跳转请求,校验签名有效后直接为当前设备/浏览器创建有效会话
    from django.core import signing
    from django.shortcuts import redirect
    from django.contrib.auth import login
    from django.contrib.auth.models import User
    
    def verify_callback(request):
        signed_payload = request.GET.get("signed")
        if not signed_payload:
            return redirect("/verify")
        try:
            # 校验签名有效性,同时限制最大有效期为1天
            payload = signing.loads(signed_payload, max_age=3600*24)
        except signing.BadSignature:
            # 签名篡改/过期,跳回验证页
            return redirect("/verify")
        # 签名有效,为当前设备创建会话
        user = User.objects.get(id=payload["user_id"])
        login(request, user)
        # 跳转到业务需要的原页面
        return redirect("/目标页面")
    

方案2:Request ID存储方案实现

如果需要携带的payload内容较多,不适合放在URL参数中,可以选择你提到的存储request ID的方案:

实现步骤

  • 验证通过后存储payload并生成加密链接
    import uuid
    from django.core import signing
    from django.core.cache import cache
    
    payload = {
        "user_id": 123,
        "verify_passed": True,
        "target_url": "/原页面路径"
    }
    # 生成唯一request ID
    request_id = str(uuid.uuid4())
    # 将payload存入缓存,设置24小时过期
    cache.set(f"verify_request:{request_id}", payload, timeout=3600*24)
    # 加密request ID防止被遍历
    encrypted_request_id = signing.dumps(request_id)
    # 拼接跳转链接
    redirect_url = f"https://你的域名.com/verify/callback?req_id={encrypted_request_id}"
    
  • 回调页解密查询payload
    from django.core import signing
    from django.shortcuts import redirect
    from django.core.cache import cache
    from django.contrib.auth import login
    from django.contrib.auth.models import User
    
    def verify_callback(request):
        encrypted_req_id = request.GET.get("req_id")
        if not encrypted_req_id:
            return redirect("/verify")
        try:
            request_id = signing.loads(encrypted_req_id, max_age=3600*24)
        except signing.BadSignature:
            return redirect("/verify")
        # 从缓存查询对应payload
        payload = cache.get(f"verify_request:{request_id}")
        if not payload:
            return redirect("/verify")
        # 为当前设备创建会话
        user = User.objects.get(id=payload["user_id"])
        login(request, user)
        # 用完删除缓存,防止链接被重复使用
        cache.delete(f"verify_request:{request_id}")
        return redirect(payload["target_url"])
    

注意事项

  • 所有签名必须设置有效期,避免信息泄露后被长期滥用
  • 涉及敏感操作的场景,签名/request ID使用后立即做失效处理,防止重复点击链接重复使用
  • 不要在payload中存储密码等敏感信息,签名仅实现防篡改能力,不属于加密存储

内容的提问来源于stack exchange,提问作者ninjacode

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.30 19:09:03