上链ON-CHAIN BLOCK 19 / 26
卷四 · dApp 前端BLOCK 19

钱包连接与签名


dApp 没有"注册/登录"表单,取而代之的是"连接钱包"。这一章讲清楚连接背后的标准接口(EIP-1193)、三种签名的本质区别,以及一个优雅的"用以太坊登录"方案。理解签名的类型至关重要——因为最贵的教训往往来自"用户不知道自己签了什么"。

连接:EIP-1193 注入的 provider

安装了 MetaMask 后,它会往网页里注入一个 window.ethereum 对象,遵循 EIP-1193 标准接口。连接就是请求授权访问用户地址:

JavaScript · 连接钱包
if (!window.ethereum) throw new Error('请安装钱包');

// 弹出钱包,请求用户授权连接
const accounts = await window.ethereum.request({
  method: 'eth_requestAccounts',
});
const address = accounts[0];

// 监听用户切换账户 / 网络,及时更新 UI
window.ethereum.on('accountsChanged', (a) => { /* 重新加载 */ });
window.ethereum.on('chainChanged',   (id) => { /* 检查链是否对 */ });
风险记账

"连接钱包"只是让 dApp 读到你的地址,它不给 dApp 动你资产的权力——连接本身零风险,资产转移永远需要你后续对具体交易签名。别被"连接了就会被盗"的谣言吓到;真正的风险在于你连接后签署了什么。同时要处理多钱包共存(EIP-6963)和用户中途切网络的情况。

三种签名,天差地别

钱包能签名的东西分几类,风险递增,务必分清:

类型用户签的是会花钱吗典型用途
personal_sign一段纯文本消息不会(链下)证明"我拥有这个地址",如登录
eth_signTypedData_v4(EIP-712)结构化的、人类可读的数据不会(链下)授权、订单、许可(permit)
发送交易一笔会上链的交易会,花 gas 改状态转账、调合约

personal_sign:证明"我是我"

JavaScript · 签名一段文本
const message = 'Sign in to MyDApp at 2026-07-06T…';
const signature = await window.ethereum.request({
  method: 'personal_sign',
  params: [message, address],
});
// 后端用 ecrecover 从签名恢复出地址,验证 == address 即证明持有私钥

它不花 gas、不上链,只是用私钥对一段话签名。后端可以从签名反推出签名者地址(第 3 章公私钥的逆用),从而确认"这个人确实控制着这个地址",无需密码。

EIP-712:让"签什么"看得懂

盲签一串十六进制是灾难之源。EIP-712 让钱包把签名内容展示成结构化、带字段名的样子,用户能看清自己在授权什么:

EIP-712 · typed data 结构
{
  domain: { name: "MyDApp", chainId: 1, verifyingContract: "0x…" },
  types: {
    Order: [
      { name: "maker",  type: "address" },
      { name: "amount", type: "uint256" },
      { name: "expiry", type: "uint256" }
    ]
  },
  message: { maker: "0xAlice…", amount: "100", expiry: "1720000000" }
}

钱包会把它显示成"maker: 0xAlice…, amount: 100, expiry: …"而不是一坨字节。它常用于链下签名、链上验证的模式:比如 DEX 的挂单(用户签一个订单,不上链、零 gas,撮合时才由合约验证并执行),或 ERC-20 的 permit(签一个授权,省掉单独的 approve 交易)。

风险记账

钓鱼的重灾区正是诱导用户签一个看似无害、实则是恶意授权的 EIP-712 消息——比如一个伪装成"验证钱包"的签名,实际是把你 NFT 的操作权授予攻击者。用户教育的铁律:看清楚每一个签名请求的 domain(是不是你信任的站点)和内容,任何看不懂的签名都拒绝。作为开发者,你有责任让签名内容清晰,并绝不训练用户养成盲签习惯。

SIWE:用以太坊登录

Sign-In with Ethereum(EIP-4361)把 personal_sign 标准化成一套安全的登录协议。流程:

  1. 后端生成一段带 nonce(随机数,防重放)、域名、时间的标准消息。
  2. 用户用钱包 personal_sign 这段消息(零 gas)。
  3. 后端验证签名 + nonce,确认无误后签发一个 session(如 JWT)。
Android 类比

SIWE 就是把"公私钥"当作登录凭证的挑战-响应认证,概念上和你熟悉的"服务器发 challenge、客户端用密钥签名、服务器验签发 token"一模一样(类似某些无密码登录 / 设备证明)。区别是身份不由某个中心账户系统颁发,而是用户自带的钱包地址。nonce 就是那个防重放的一次性挑战值,和你做安全认证时的思路完全一致。

要点入账

连接钱包(EIP-1193)只暴露地址,零风险;风险全在之后签了什么。三类签名要分清:personal_sign(证明身份、链下)、EIP-712(结构化可读、常用于链下授权)、发送交易(上链花钱)。SIWE 用签名实现无密码登录。作为工程师,你的天职是让每个签名请求都清晰可懂,永不诱导盲签。下一章我们把连接、读、写、事件监听全串起来,做一个完整可跑的 dApp。

本章小结

◆ 返回目录