🎉 攢成長值,抽華爲Mate三折疊!廣場第 1️⃣ 2️⃣ 期夏季成長值抽獎大狂歡開啓!
總獎池超 $10,000+,華爲Mate三折疊手機、F1紅牛賽車模型、Gate限量週邊、熱門代幣等你來抽!
立即抽獎 👉 https://www.gate.com/activities/pointprize?now_period=12
如何快速賺成長值?
1️⃣ 進入【廣場】,點擊頭像旁標識進入【社區中心】
2️⃣ 完成發帖、評論、點讚、發言等日常任務,成長值拿不停
100%有獎,抽到賺到,大獎等你抱走,趕緊試試手氣!
截止於 8月9日 24:00 (UTC+8)
詳情: https://www.gate.com/announcements/article/46384
#成长值抽奖12期开启#
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 });
這樣可以顯著減少驗證時間,提高接口速度。