📢 Gate广场 #MBG任务挑战# 发帖赢大奖活动火热开启!
想要瓜分1,000枚MBG?现在就来参与,展示你的洞察与实操,成为MBG推广达人!
💰️ 本期将评选出20位优质发帖用户,每人可轻松获得50枚MBG!
如何参与:
1️⃣ 调研MBG项目
对MBG的基本面、社区治理、发展目标、代币经济模型等方面进行研究,分享你对项目的深度研究。
2️⃣ 参与并分享真实体验
参与MBG相关活动(包括CandyDrop、Launchpool或现货交易),并晒出你的参与截图、收益图或实用教程。可以是收益展示、简明易懂的新手攻略、小窍门,也可以是现货行情点位分析,内容详实优先。
3️⃣ 鼓励带新互动
如果你的帖子吸引到他人参与活动,或者有好友评论“已参与/已交易”,将大幅提升你的获奖概率!
MBG热门活动(帖文需附下列活动链接):
Gate第287期Launchpool:MBG — 质押ETH、MBG即可免费瓜分112,500 MBG,每小时领取奖励!参与攻略见公告:https://www.gate.com/announcements/article/46230
Gate CandyDrop第55期:CandyDrop x MBG — 通过首次交易、交易MBG、邀请好友注册交易即可分187,500 MBG!参与攻略见公告:https://www.gate.com/announcements
SIWE:以太坊登录新标准实践指南
SIWE:以太坊登录的强大工具
SIWE (Sign-In with Ethereum) 是一种在以太坊上验证用户身份的方法。类似于发起交易,它通过签名来证明用户对钱包的控制权。目前,大多数主流钱包插件都已支持这种简单的身份验证方式。
本文主要讨论以太坊上的签名场景,不涉及其他公链如 Solana、SUI 等。
什么时候需要 SIWE?
如果你的 Dapp 具有以下需求,可以考虑使用 SIWE:
对于主要提供查询功能的 Dapp(如区块浏览器),可能并不需要 SIWE。
虽然通过钱包连接可以在前端证明身份,但对于需要后端支持的 API 调用,仅传递地址是不够的,因为地址是公开信息,任何人都可以使用。
SIWE 的原理和流程
SIWE 的流程可以概括为三个步骤:连接钱包 - 签名 - 获取身份标识。
连接钱包
这是 Web3 应用中常见的操作,通过钱包插件连接用户的钱包。
签名
签名步骤包括获取 Nonce 值、钱包签名和后端签名校验。
首先从后端获取随机生成的 Nonce 值,后端会将其与当前地址关联。
前端获取 Nonce 值后,构建签名内容,包括 Nonce、域名、链 ID 等信息,然后使用钱包的签名方法进行签名。
最后将签名发送给后端进行验证。
获取身份标识
后端验证签名通过后,会返回用户身份标识(如 JWT)。前端后续请求时带上地址和身份标识,即可证明对钱包的所有权。
实践 SIWE
下面我们通过一个简单的 Demo 来实践 SIWE 的基本流程,目标是让 Dapp 能返回用于身份校验的 JWT。
注意:此 Demo 仅用于展示基本流程,生产环境使用可能存在安全隐患。
准备工作
我们使用 Next.js 开发全栈应用,需要准备 Node.js 环境。
安装依赖
首先安装 Next.js:
npx create-next-app@14
按提示完成安装。
进入项目目录并启动:
npm run dev
访问 localhost:3000 可以看到基本的 Next.js 项目。
安装 SIWE 相关依赖
我们使用 Ant Design Web3 来连接钱包并实现 SIWE 功能:
npm install antd @ant-design/web3 @ant-design/web3-wagmi wagmi viem @tanstack/react-query --save
引入 Wagmi
在 layout.tsx 中引入 WagmiProvider:
jsx "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 siwe="{{" 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;
然后添加连接钱包的按钮:
jsx "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 登录框架。
接口实现
现在来实现后端需要的一些接口。
Nonce
生成随机 Nonce 并与地址关联:
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, }); }
verifyMessage
验证签名并返回 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);
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"); }
const token = jwt.sign({ address }, JWT_SECRET, { expiresIn: "1h" }); return Response.json({ data: token, }); }
至此,一个基本的 SIWE 登录 Dapp 就开发完成了。
性能优化
为提高验证速度,建议使用专门的节点服务。这里我们使用 ZAN 的节点服务,替换 publicClient 的默认 RPC:
javascript const publicClient = createPublicClient({ chain: mainnet, transport: http('), //替换为获取到的 ZAN 节点服务 RPC });
这样可以显著减少验证时间,提高接口速度。