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

如何实现外部应用用户免密登录Django应用并自动创建用户?

方案选型建议

你的需求是信任双边应用间的无凭证自动登录+用户自动创建,不需要第三方身份提供商,所以基于共享密钥的HMAC签名验证方案是最适合的——比SSO/OAuth轻量得多,不需要复杂的授权流程,同时能满足安全性要求。

为什么不选SSO/OAuth?

  • SSO需要独立的身份提供商(IDP),你们没有第三方账号需求,搭建IDP反而增加复杂度
  • OAuth是授权框架,适合跨平台的第三方授权场景,这里是双边信任集成,用OAuth属于过度设计

你最初设想的“加密Token”思路方向正确,但更推荐用签名验证而非加密:加密是为了保密数据,而你们的核心需求是验证请求来自可信的第三方、数据未被篡改,签名(比如HMAC)更直接且性能更好。

双方应用职责划分

第三方发起应用

  • 触发跳转:提供用户点击的按钮,触发向你的Django应用跳转的逻辑
  • 生成请求参数:整理用户邮箱、必要的用户附加数据(如部门、姓名),添加时间戳(用于防止重放攻击)
  • 生成签名:用双方约定的共享密钥,对参数集合进行HMAC签名
  • 构造跳转URL:将用户数据、时间戳、签名作为GET参数拼接在你的授权回调URL后

你的Django应用

  • 提供授权回调视图:专门处理第三方跳转过来的请求
  • 签名验证:按照相同规则重新计算签名,与请求中的签名比对,同时验证时间戳有效性
  • 用户管理:根据邮箱查询用户,不存在则自动创建(使用Django原生User模型或自定义用户模型)
  • 自动登录:验证通过后,为用户创建Django会话,完成自动登录
  • 页面跳转:登录完成后跳转到指定页面(如应用首页)
具体实现指导

1. 准备共享密钥

双方约定一个强密钥,分别存在各自应用的环境变量中,不要硬编码。比如在你的Django项目中,在settings.py里:

import os
SHARED_SECRET_KEY = os.environ.get('SHARED_INTEGRATION_SECRET')

2. 第三方应用的签名生成逻辑(示例,以Python为例)

import hmac
import hashlib
import time
import urllib.parse
import json

def generate_auth_url(django_callback_url, user_email, user_data, secret_key):
    # 1. 准备参数,必须包含时间戳
    timestamp = str(int(time.time()))
    params = {
        'email': user_email,
        'user_data': json.dumps(user_data),  # 附加数据转JSON字符串
        'timestamp': timestamp
    }
    # 2. 按照固定顺序拼接参数(双方必须一致,否则签名会不一致)
    sorted_params = sorted(params.items())
    param_string = '&'.join([f"{k}={urllib.parse.quote(v)}" for k, v in sorted_params])
    # 3. 生成HMAC-SHA256签名
    signature = hmac.new(
        secret_key.encode('utf-8'),
        param_string.encode('utf-8'),
        hashlib.sha256
    ).hexdigest()
    # 4. 构造最终跳转URL
    params['signature'] = signature
    return f"{django_callback_url}?{urllib.parse.urlencode(params)}"

3. Django端的授权回调视图实现

from django.shortcuts import redirect
from django.contrib.auth import login, get_user_model
from django.http import HttpResponseForbidden
import hmac
import hashlib
import urllib.parse
import time
import json
from django.conf import settings

User = get_user_model()

def third_party_auth(request):
    # 1. 获取请求参数
    email = request.GET.get('email')
    user_data_str = request.GET.get('user_data')
    timestamp = request.GET.get('timestamp')
    signature = request.GET.get('signature')

    # 2. 参数合法性检查
    if not all([email, timestamp, signature]):
        return HttpResponseForbidden("Invalid request parameters")

    # 3. 验证时间戳(防止重放攻击,比如限制5分钟内有效)
    try:
        timestamp_int = int(timestamp)
        if time.time() - timestamp_int > 300:  # 300秒=5分钟
            return HttpResponseForbidden("Request expired")
    except ValueError:
        return HttpResponseForbidden("Invalid timestamp")

    # 4. 重新计算签名,与请求中的签名比对
    params_to_sign = {
        'email': email,
        'user_data': user_data_str,
        'timestamp': timestamp
    }
    sorted_params = sorted(params_to_sign.items())
    param_string = '&'.join([f"{k}={urllib.parse.quote(v)}" for k, v in sorted_params])
    computed_signature = hmac.new(
        settings.SHARED_SECRET_KEY.encode('utf-8'),
        param_string.encode('utf-8'),
        hashlib.sha256
    ).hexdigest()

    if computed_signature != signature:
        return HttpResponseForbidden("Invalid signature")

    # 5. 处理用户:存在则获取,不存在则创建
    user_data = json.loads(user_data_str) if user_data_str else {}
    user, created = User.objects.get_or_create(
        email=email,
        defaults={
            'username': email.split('@')[0],  # 用邮箱前缀作为默认用户名
            'first_name': user_data.get('first_name', ''),
            'last_name': user_data.get('last_name', '')
        }
    )

    # 6. 自动登录用户
    login(request, user)

    # 7. 跳转到目标页面(比如首页)
    return redirect('/')

4. 配置Django URL

在项目的urls.py中添加回调URL:

from django.urls import path
from .views import third_party_auth

urlpatterns = [
    # ... 其他URL
    path('auth/third-party/', third_party_auth, name='third_party_auth'),
]

安全注意事项

  • 必须使用HTTPS传输所有请求,防止参数被窃听或篡改
  • 时间戳验证不可省略,避免攻击者重复使用旧的合法请求
  • 共享密钥要定期轮换,且不要在任何日志或代码中泄露
  • 限制用户数据的范围,只传输必要信息,不要包含敏感数据
  • 如果你的Django使用自定义用户模型,调整get_or_create中的字段即可

内容的提问来源于stack exchange,提问作者Aikanáro

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 20:42:44