使用 Django 与 Web3 构建去中心化身份验证系统

Posted by AGA链讯 on February 24, 2025

在当今的 Web 开发领域,去中心化身份验证系统正变得越来越受欢迎。借助区块链技术,我们可以打造出安全、透明且无需依赖中心化第三方服务提供商的全新身份验证方式。

本文将详细介绍如何利用 Django 作为后端框架,结合 Web3 技术实现与以太坊区块链的交互,从而构建一个功能完备的去中心化身份验证系统。


核心概念与优势

去中心化身份验证基于区块链的密码学原理,具备以下突出优点:

  • 更高的安全性:利用非对称加密和数字签名技术,有效防范钓鱼攻击和中间人攻击。
  • 用户自主权:用户完全掌控自己的身份信息,无需将敏感数据托付给中心化机构。
  • 透明与不可篡改:所有的验证操作都记录在公开透明的区块链上,极大地增强了系统的可信度。
  • 无需密码:用户无需记忆复杂的密码,仅通过其加密钱包密钥对即可完成验证。

准备工作与环境配置

在开始构建之前,请确保你的开发环境中已安装以下必要的工具和库:

  • Python 3.x
  • Django 3.x 或更高版本
  • Web3.py 库(用于与以太坊节点交互)
  • 一个可用的以太坊节点连接(可以使用 Infura 等服务提供商或本地测试节点)

你可以通过 pip 轻松安装所需的 Python 包:

pip install django web3

构建步骤详解

第一步:创建 Django 项目与应用

首先,使用命令行创建一个新的 Django 项目:

django-admin startproject decentralized_auth

创建完成后,进入项目目录并创建一个专门用于处理身份验证的应用:

cd decentralized_auth
python manage.py startapp auth

第二步:定义用户模型

auth/models.py 文件中,我们需要定义一个自定义的用户模型。与传统系统不同,此模型将不存储密码,而是关联用户的以太坊公钥地址。

from django.db import models

class User(models.Model):
    username = models.CharField(max_length=50, unique=True)
    public_address = models.CharField(max_length=42, unique=True) # 标准的以太坊地址长度
    date_joined = models.DateTimeField(auto_now_add=True)

定义模型后,记得运行 makemigrationsmigrate 命令来创建数据库表。

第三步:实现签名验证逻辑

去中心化身份验证的核心是验证用户对其地址的所有权。这通过验证一条由用户私钥签名的消息来实现。

auth/utils.py 中创建一个关键的验证函数:

from eth_account.messages import encode_defunct
from web3 import Web3, Account

def verify_signature(address, signature, message):
    """
    验证以太坊签名
    :param address: 声称的以太坊地址
    :param signature: hex格式的签名
    :param message: 被签名的原始消息
    :return: Boolean, 验证是否通过
    """
    # 将消息编码为以太坊标准格式
    message_hash = encode_defunct(text=message)
    try:
        # 从签名中恢复出公钥
        signer_public_key = Account.recover_message(message_hash, signature=signature)
        # 将公钥转换为校验和格式的地址
        signer_address = Web3.to_checksum_address(Account.from_key(signer_public_key).address)
        # 比较恢复出的地址与提供的地址是否一致
        return Web3.to_checksum_address(address) == signer_address
    except (ValueError, TypeError):
        return False

第四步:编写身份验证视图

接下来,在 auth/views.py 中创建处理登录请求的视图。这个视图将接收用户地址、签名和原始消息,并进行验证。

from django.http import JsonResponse
from django.views.decorators.csrf import csrf_exempt
from django.contrib.auth import login
from .models import User
from .utils import verify_signature

@csrf_exempt # 注意:在生产环境中应谨慎处理CSRF,此处为演示简化
def web3_login(request):
    if request.method == 'POST':
        address = request.POST.get('address')
        signature = request.POST.get('signature')
        message = request.POST.get('message')

        # 调用验证函数
        if verify_signature(address, signature, message):
            # 验证成功,获取或创建用户
            user, created = User.objects.get_or_create(public_address=address)
            if created:
                # 可以为新用户生成一个随机用户名
                user.username = f'user_{user.id}'
                user.save()
            # 调用Django的login函数完成登录
            login(request, user)
            return JsonResponse({'status': 'success', 'message': '登录成功!'})
        else:
            return JsonResponse({'status': 'error', 'message': '签名验证失败!'}, status=401)
    return JsonResponse({'status': 'error', 'message': '仅支持POST请求'}, status=405)

第五步:设计前端登录界面

在前端,你需要一个页面来触发 MetaMask 等钱包的签名操作,并将签名结果发送到后端。以下是 login.html 模板的一个简化示例:

<!DOCTYPE html>
<html>
<head>
    <title>Web3 登录</title>
    <script src="https://cdn.jsdelivr.net/npm/web3@1.6.0/dist/web3.min.js"></script>
</head>
<body>
    <h2>使用以太坊账户登录</h2>
    <button onclick="requestAccount()">连接钱包</button>
    <button onclick="signMessage()">签名并登录</button>

    <script>
        let userAddress;
        async function requestAccount() {
            if (window.ethereum) {
                try {
                    const accounts = await window.ethereum.request({ method: 'eth_requestAccounts' });
                    userAddress = accounts[0];
                    console.log("连接的地址:", userAddress);
                } catch (error) {
                    console.error("用户拒绝连接钱包");
                }
            } else {
                alert("请安装 MetaMask!");
            }
        }

        async function signMessage() {
            if (!userAddress) {
                alert("请先连接钱包");
                return;
            }
            const message = "请签名此消息以登录网站。随机数: " + Math.random().toString(36).substring(2);
            try {
                const signature = await window.ethereum.request({
                    method: 'personal_sign',
                    params: [message, userAddress],
                });
                // 将地址、签名和消息发送到后端进行验证
                sendToDjango(userAddress, signature, message);
            } catch (error) {
                console.error("签名失败:", error);
            }
        }

        function sendToDjango(address, signature, message) {
            // 使用FormData或fetch API将数据POST到你的Django视图
            const formData = new FormData();
            formData.append('address', address);
            formData.append('signature', signature);
            formData.append('message', message);

            fetch('/auth/login/', {
                method: 'POST',
                body: formData
            })
            .then(response => response.json())
            .then(data => {
                if (data.status === 'success') {
                    alert('登录成功!');
                    // 可在此处进行页面跳转
                } else {
                    alert('登录失败: ' + data.message);
                }
            });
        }
    </script>
</body>
</html>

部署与安全考量

将此类系统投入生产环境时,务必注意以下几点:

  • 消息内容:签名的消息应包含一个随机数(Nonce)和特定于本次会话的标识,以防止重放攻击。
  • CSRF 保护:示例中使用了 @csrf_exempt 进行简化,在实际应用中,应设计适合 API 的 CSRF 保护机制。
  • 节点依赖:确保你的 Web3 提供商(如 Infura)稳定可靠,并处理好可能出现的连接超时或错误。
  • 用户体验:引导用户完成钱包连接和签名的流程,并提供清晰的操作反馈。

👉 查看完整的实时开发工具与资源列表

常见问题

Q1: 去中心化身份验证和传统的 OAuth 登录(如微信、Google 登录)有什么区别? A1: 最根本的区别在于信任模型。OAuth 依赖中心化的第三方平台来验证用户身份,而你信任这些平台。而去中心化身份验证则基于密码学原理,用户通过自己的私钥证明所有权,无需依赖任何中间人,真正实现了“用户即主权”。

Q2: 如果用户丢失了私钥或助记词怎么办? A2: 这是一个关键问题。在纯粹的 Web3 模型中,私钥是身份的唯一凭证,丢失即意味着无法恢复账户。因此,必须教育用户使用硬件钱包、多重签名或可靠的助记词备份方案来妥善保管密钥。一些项目也在探索社交恢复等更友好的解决方案。

Q3: 这个系统需要支付 Gas 费吗? A3: 不需要。我们的验证过程仅涉及对链下消息的签名和验证(off-chain signature),这个过程不需要向区块链提交交易,因此用户无需支付任何 Gas 费,体验流畅且成本为零。

Q4: 能否将这种验证方式与 Django 原有的基于 Session 的认证系统一起使用? A4: 完全可以。本文示例正是在 Django 的认证框架基础上构建的。登录成功后,我们调用了 login(request, user),这意味着该用户会话会被纳入 Django 的原生 session 管理之中,你可以像对待任何其他已登录用户一样使用 @login_required 等装饰器来保护视图。

Q5: 除了登录,这种技术还能用在哪些场景? A5: 其应用非常广泛。任何需要验证用户身份或权限的场景都可以使用,例如:确认数字资产所有权、进行去中心化投票、登录链上游戏角色、验证链下数据的真实性(例如,持有特定 NFT 的用户才能访问专属内容)等。


总结而言,利用 Django 和 Web3 构建去中心化身份验证系统,不仅能够提升应用的安全性,更是迈向下一代 Web(Web3)应用的重要一步。虽然初期开发复杂度有所增加,但它所带来的用户信任度和安全收益是传统方案无法比拟的。

👉 探索更多高级区块链开发策略与实战案例