📢 Gate广场 #创作者活动第一期# 火热开启,助力 PUMP 公募上线!
Solana 爆火项目 Pump.Fun($PUMP)现已登陆 Gate 平台开启公开发售!
参与 Gate广场创作者活动,释放内容力量,赢取奖励!
📅 活动时间:7月11日 18:00 - 7月15日 22:00(UTC+8)
🎁 活动总奖池:$500 USDT 等值代币奖励
✅ 活动一:创作广场贴文,赢取优质内容奖励
📅 活动时间:2025年7月12日 22:00 - 7月15日 22:00(UTC+8)
📌 参与方式:在 Gate 广场发布与 PUMP 项目相关的原创贴文
内容不少于 100 字
必须带上话题标签: #创作者活动第一期# #PumpFun#
🏆 奖励设置:
一等奖(1名):$100
二等奖(2名):$50
三等奖(10名):$10
📋 评选维度:Gate平台相关性、内容质量、互动量(点赞+评论)等综合指标;参与认购的截图的截图、经验分享优先;
✅ 活动二:发推同步传播,赢传播力奖励
📌 参与方式:在 X(推特)上发布与 PUMP 项目相关内容
内容不少于 100 字
使用标签: #PumpFun # Gate
发布后填写登记表登记回链 👉 https://www.gate.com/questionnaire/6874
🏆 奖励设置:传播影响力前 10 名用户,瓜分 $2
SIWE技术助力Dapp提升用户身份验证
SIWE:提升 Dapp 功能的关键技术
SIWE(Sign-In with Ethereum)是一种在以太坊上验证用户身份的方法,类似于发起钱包交易,证明用户对钱包拥有控制权。目前的身份验证方式非常简单,只需在钱包插件中对信息进行签名即可,大多数常见钱包插件都已支持。
本文主要讨论以太坊上的签名场景,不涉及其他区块链如 Solana、SUI 等。
是否需要 SIWE
如果你的 Dapp 具有以下需求,可以考虑使用 SIWE:
但如果你的 Dapp 主要是查询功能,比如类似 etherscan 的应用,可以不使用 SIWE。
你可能会疑惑,在 Dapp 上通过钱包连接后,不就已经证明了钱包所有权吗?这个说法部分正确。对前端而言,钱包连接确实表明了身份,但对于需要后端支持的接口调用,仅传递地址是不够的,因为地址是公开信息,任何人都可以"借用"。
SIWE 的原理和流程
SIWE 流程可概括为三个步骤:连接钱包 - 签名 - 获取身份标识。让我们详细了解这三个步骤。
连接钱包
连接钱包是常见的 Web3 操作,通过钱包插件可以在 Dapp 中连接钱包。
签名
SIWE 中的签名步骤包括获取 Nonce 值、钱包签名和后端签名校验。
获取 Nonce 值需要调用后端接口。后端收到请求后,会生成随机 Nonce 值并与当前地址关联,为后续签名做准备。
前端获取 Nonce 值后,需要构建签名内容,包括 Nonce 值、域名、链 ID、签名内容等,通常使用钱包提供的签名方法进行签名。
构建完签名后,将其发送给后端。
获取身份标识
后端校验签名通过后,会返回用户身份标识,如 JWT。前端后续请求时带上对应地址和身份标识,即可证明钱包所有权。
实践
现有许多组件和库支持快速接入钱包连接和 SIWE。我们的目标是让 Dapp 能返回 JWT 用于用户身份校验。注意,此 demo 仅用于介绍 SIWE 基本流程,生产环境使用可能存在安全问题。
准备工作
本文使用 Next.js 开发应用,需要 Node.js 环境。使用 Next.js 的好处是可以直接开发全栈项目,无需分为前后端两个项目。
安装依赖
首先安装 Next.js,在项目目录中执行:
npx create-next-app@14
按提示安装完成后,进入项目目录并运行:
npm run dev
根据终端提示,访问 localhost:3000 即可看到基本的 Next.js 项目。
安装 SIWE 相关依赖
SIWE 需要登录体系,因此需要连接钱包。这里我们使用 Ant Design Web3,因为:
在终端执行:
npm install antd @ant-design/web3 @ant-design/web3-wagmi wagmi viem @tanstack/react-query --save
引入 Wagmi
Ant Design Web3 的 SIWE 依赖 Wagmi 库实现。我们需要在 layout.tsx 中引入相关 Provider,使整个项目都可以使用 Wagmi 提供的 Hooks。
首先定义 WagmiProvider 配置:
javascript "use client"; import { getNonce, verifyMessage } from "@/app/api"; import { Mainnet, MetaMask, OkxWallet, TokenPocket, WagmiWeb3ConfigProvider, WalletConnect, } from "@ant-design/web3-wagmi"; import { QueryClient } from "@tanstack/react-query"; import React from "react"; import { createSiweMessage } from "viem/siwe"; import { http } from "wagmi"; import { JwtProvider } from "./JwtProvider";
const YOUR_WALLET_CONNECT_PROJECT_ID = "c07c0051c2055890eade3556618e38a6"; const queryClient = new QueryClient();
const WagmiProvider: React.FC = ({ children }) => { const [jwt, setJwt] = React.useState(null);
return ( <wagmiweb3configprovider siweconfig="{{" getnonce:="" async="" (address)=""> (await getNonce(address)).data, createMessage: (props) => { return createSiweMessage({ ...props, statement: "Ant Design Web3" }); }, verifyMessage: async (message, signature) => { const jwt = (await verifyMessage(message, signature)).data; setJwt(jwt); return !!jwt; }, }} chains={[Mainnet]} transports={{ [Mainnet.id]: http(), }} walletConnect={{ projectId: YOUR_WALLET_CONNECT_PROJECT_ID, }} wallets={[ MetaMask(), WalletConnect(), TokenPocket({ group: "Popular", }), OkxWallet(), ]} queryClient={queryClient} > {children} ); };
export default WagmiProvider;
然后添加连接钱包按钮,这样就在前端添加了连接入口。至此已经接入了 SIWE,步骤非常简单。
接下来定义连接按钮,实现连接钱包和签名:
javascript "use client"; import type { Account } from "@ant-design/web3"; import { ConnectButton, Connector } from "@ant-design/web3"; import { Flex, Space } from "antd"; import React from "react"; import { JwtProvider } from "./JwtProvider";
export default function App() { const jwt = React.useContext(JwtProvider);
const renderSignBtnText = ( defaultDom: React.ReactNode, account?: Account ) => { const { address } = account ?? {}; const ellipsisAddress = address ? ${address.slice(0, 6)}...${address.slice(-6)} : ""; return Sign in as ${ellipsisAddress}; };
return ( <>
这样就实现了一个最简单的 SIWE 登录框架。
接口实现
SIWE 需要一些接口来帮助后端校验用户身份。现在我们来简单实现一下。
Nonce
Nonce 用于让钱包每次签名时生成的内容变化,提高签名可靠性。Nonce 生成需要与用户传入的 address 关联,提高验证准确性。
Nonce 实现非常直接,首先生成随机字符串(由字母和数字组成),然后将 nonce 和 address 建立联系:
javascript import { randomBytes } from "crypto"; import { addressMap } from "../cache";
export async function GET(request: Request) { const { searchParams } = new URL(request.url); const address = searchParams.get("address");
if (!address) { throw new Error("Invalid address"); } const nonce = randomBytes(16).toString("hex"); addressMap.set(address, nonce); return Response.json({ data: nonce, }); }
signMessage
signMessage 用于签名内容,这部分功能通常由钱包插件完成,我们一般不需要配置,只需指定方法即可。本 demo 中使用 Wagmi 的签名方法。
verifyMessage
用户签名后,需将签名前内容和签名一同发给后端校验。后端从签名中解析出对应内容进行比较,一致则表示验证通过。
此外,还需对签名内容进行安全性校验,如签名内容中的 Nonce 值是否与派发给用户的一致等。验证通过后,需返回用户 JWT 用于后续权限校验,示例代码如下:
javascript import { createPublicClient, http } from "viem"; import { mainnet } from "viem/chains"; import jwt from "jsonwebtoken"; import { parseSiweMessage } from "viem/siwe"; import { addressMap } from "../cache";
const JWT_SECRET = "your-secret-key"; // 请使用更安全的密钥,并添加对应的过期校验等
const publicClient = createPublicClient({ chain: mainnet, transport: http(), });
export async function POST(request: Request) { const { signature, message } = await request.json();
const { nonce, address = "0x" } = parseSiweMessage(message); console.log("nonce", nonce, address, addressMap);
// 校验 nonce 值是否一致 if (!nonce || nonce !== addressMap.get(address)) { throw new Error("Invalid nonce"); }
// 校验签名内容 const valid = await publicClient.verifySiweMessage({ message, address, signature, });
if (!valid) { throw new Error("Invalid signature"); }
// 生成 jwt 并返回 const token = jwt.sign({ address }, JWT_SECRET, { expiresIn: "1h" }); return Response.json({ data: token, }); }
至此,一个基本实现 SIWE 登录的 Dapp 就开发完成了。
优化建议
使用默认 RPC 节点进行 SIWE 登录验证可能需要近 30 秒,因此强烈建议使用专门的节点服务来提升接口响应时间。本文使用 ZAN 的节点服务,可以从 ZAN 节点服务控制台获取对应 RPC 连接。
获取以太坊主网的 HTTPS RPC 连接后,在代码中替换 publicClient 的默认 RPC:
javascript const publicClient = createPublicClient({ chain: mainnet, transport: http('), //获取到的 ZAN 节点服务 RPC });
替换后,验证时间将显著减少,接口速度明显加快。