SIWE (Đăng nhập bằng Ethereum) là một phương pháp xác thực danh tính người dùng trên Ethereum, tương tự như việc khởi động giao dịch ví, chứng minh người dùng có quyền kiểm soát ví. Phương pháp xác thực hiện tại rất đơn giản, chỉ cần ký thông tin trong plugin ví, hầu hết các plugin ví phổ biến đều đã hỗ trợ.
Bài viết này chủ yếu thảo luận về các tình huống ký trên Ethereum, không đề cập đến các chuỗi blockchain khác như Solana, SUI, v.v.
Có cần SIWE không
Nếu Dapp của bạn có các yêu cầu sau, hãy xem xét việc sử dụng SIWE:
Có hệ thống người dùng riêng
Cần tra cứu thông tin liên quan đến quyền riêng tư của người dùng
Nhưng nếu Dapp của bạn chủ yếu là chức năng tra cứu, chẳng hạn như ứng dụng giống như etherscan, bạn có thể không sử dụng SIWE.
Bạn có thể thắc mắc, sau khi kết nối ví trên Dapp, chẳng phải đã chứng minh quyền sở hữu ví rồi sao? Câu này đúng một phần. Đối với phía trước, kết nối ví thực sự cho thấy danh tính, nhưng đối với các gọi API cần hỗ trợ phía sau, chỉ truyền địa chỉ là không đủ, vì địa chỉ là thông tin công khai, bất kỳ ai cũng có thể "mượn".
Nguyên lý và quy trình của SIWE
Quy trình SIWE có thể được tóm tắt thành ba bước: Kết nối ví - Ký tên - Nhận danh tính. Hãy cùng tìm hiểu chi tiết về ba bước này.
Kết nối ví
Kết nối ví là thao tác Web3 phổ biến, thông qua plugin ví có thể kết nối ví trong Dapp.
chữ ký
Các bước ký trong SIWE bao gồm lấy giá trị Nonce, ký ví và xác minh chữ ký từ phía máy chủ.
Để lấy giá trị Nonce, cần gọi API backend. Sau khi backend nhận được yêu cầu, nó sẽ tạo ra giá trị Nonce ngẫu nhiên và liên kết với địa chỉ hiện tại, chuẩn bị cho việc ký sau này.
Sau khi nhận được giá trị Nonce từ phía trước, cần xây dựng nội dung ký, bao gồm giá trị Nonce, tên miền, ID chuỗi, nội dung ký, v.v., thường sử dụng phương pháp ký do ví cung cấp.
Sau khi xây dựng xong chữ ký, hãy gửi nó cho backend.
Lấy danh tính
Sau khi kiểm tra chữ ký ở backend thành công, sẽ trả về danh tính người dùng, chẳng hạn như JWT. Trong các yêu cầu tiếp theo từ frontend, mang theo địa chỉ tương ứng và danh tính, có thể chứng minh quyền sở hữu ví.
Thực hành
Hiện có nhiều thành phần và thư viện hỗ trợ kết nối ví và SIWE nhanh chóng. Mục tiêu của chúng tôi là cho phép Dapp trả về JWT để xác thực danh tính người dùng. Lưu ý, bản demo này chỉ dùng để giới thiệu quy trình cơ bản của SIWE, việc sử dụng trong môi trường sản xuất có thể gặp vấn đề về bảo mật.
công việc chuẩn bị
Bài viết này sử dụng Next.js để phát triển ứng dụng, cần có môi trường Node.js. Lợi ích của việc sử dụng Next.js là có thể phát triển dự án full-stack trực tiếp, không cần phải chia thành hai dự án frontend và backend.
Cài đặt phụ thuộc
Đầu tiên cài đặt Next.js, thực hiện trong thư mục dự án:
npx create-next-app@14
Sau khi cài đặt hoàn tất theo hướng dẫn, vào thư mục dự án và chạy:
npm run dev
Theo hướng dẫn của terminal, bạn có thể truy cập localhost:3000 để xem dự án Next.js cơ bản.
Cài đặt các phụ thuộc liên quan đến SIWE
SIWE cần hệ thống đăng nhập, vì vậy cần kết nối ví. Ở đây chúng tôi sử dụng Ant Design Web3, vì:
Hoàn toàn miễn phí và được bảo trì tích cực
Là một thư viện thành phần Web3, trải nghiệm sử dụng tương tự như thư viện thành phần thông thường, không có gánh nặng tâm lý bổ sung.
Ant Design Web3 của SIWE phụ thuộc vào thư viện Wagmi để thực hiện. Chúng ta cần nhập các Provider liên quan vào layout.tsx, để toàn bộ dự án có thể sử dụng các Hooks do Wagmi cung cấp.
Đầu tiên định nghĩa cấu hình WagmiProvider:
javascript
"use client";
import { getNonce, verifyMessage } from "@/app/api";
import {
Mainnet,
MetaMask,
OkxWallet,
TokenPocket,
WagmiWeb3ConfigProvider,
WalletConnect,
} từ "@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();
Sau đó, thêm nút kết nối ví, như vậy đã thêm được điểm truy cập kết nối ở phía trước. Đến đây đã kết nối SIWE, bước thực hiện rất đơn giản.
Tiếp theo định nghĩa nút kết nối, thực hiện kết nối ví và ký.
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";
xuất khẩu chức năng mặc định App() {
const jwt = React.useContext(JwtProvider);
Điều này đã tạo ra một khung đăng nhập SIWE đơn giản nhất.
triển khai giao diện
SIWE cần một số giao diện để giúp backend xác minh danh tính người dùng. Bây giờ chúng ta sẽ thực hiện một cách đơn giản.
Nonce
Nonce được sử dụng để thay đổi nội dung được tạo ra mỗi khi ví ký, tăng cường độ tin cậy của chữ ký. Việc tạo Nonce cần liên kết với địa chỉ do người dùng cung cấp, nhằm nâng cao độ chính xác của việc xác thực.
Nonce được triển khai rất đơn giản, đầu tiên tạo ra một chuỗi ngẫu nhiên ( được cấu thành từ chữ cái và số ), sau đó liên kết nonce với địa chỉ:
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");
nếu (!address) {
throw new Error("Địa chỉ không hợp lệ");
}
const nonce = randomBytes(16).toString("hex");
addressMap.set(address, nonce);
return Response.json({
dữ liệu: nonce,
});
}
signMessage
signMessage được sử dụng để ký nội dung, phần này thường được thực hiện bởi plugin ví, chúng tôi thường không cần cấu hình, chỉ cần chỉ định phương pháp là đủ. Trong demo này, chúng tôi sử dụng phương pháp ký của Wagmi.
xác minhTinNhắn
Sau khi người dùng ký, cần gửi nội dung trước khi ký và chữ ký cùng nhau cho backend để xác thực. Backend sẽ giải mã nội dung tương ứng từ chữ ký để so sánh, nếu一致 thì表示 xác thực thành công.
Ngoài ra, cần phải kiểm tra tính an toàn của nội dung chữ ký, chẳng hạn như giá trị Nonce trong nội dung chữ ký có khớp với giá trị đã phát cho người dùng hay không. Sau khi xác minh thành công, cần trả về JWT của người dùng để sử dụng cho việc kiểm tra quyền sau này, mã mẫu như sau:
javascript
import { createPublicClient, http } from "viem";
import { mainnet } từ "viem/chains";
import jwt từ "jsonwebtoken";
import { parseSiweMessage } from "viem/siwe";
import { addressMap } from "../cache";
const JWT_SECRET = "your-secret-key"; // Vui lòng sử dụng khóa an toàn hơn và thêm kiểm tra hết hạn tương ứng.
// Kiểm tra xem giá trị nonce có khớp không
nếu (!nonce || nonce !== addressMap.get(address)) {
throw new Error("Nonce không hợp lệ");
}
// Kiểm tra nội dung chữ ký
const valid = await publicClient.verifySiweMessage({
thông điệp,
địa chỉ,
chữ ký,
});
nếu (!valid) {
throw new Error("Chữ ký không hợp lệ");
}
// Tạo jwt và trả về
const token = jwt.sign({ address }, JWT_SECRET, { expiresIn: "1h" });
return Response.json({
dữ liệu: token,
});
}
Đến đây, một Dapp cơ bản để thực hiện đăng nhập SIWE đã hoàn thành.
Đề xuất tối ưu hóa
Việc xác thực đăng nhập SIWE bằng cách sử dụng nút RPC mặc định có thể mất gần 30 giây, vì vậy rất khuyến nghị sử dụng dịch vụ nút chuyên dụng để tăng tốc độ phản hồi của giao diện. Bài viết này sử dụng dịch vụ nút của ZAN, có thể lấy kết nối RPC tương ứng từ bảng điều khiển dịch vụ nút ZAN.
Sau khi nhận được kết nối HTTPS RPC của mạng chính Ethereum, hãy thay thế RPC mặc định của publicClient trong mã.
javascript
const publicClient = createPublicClient({
chuỗi: mainnet,
vận chuyển: http('), // dịch vụ RPC của nút ZAN nhận được
});
Sau khi thay thế, thời gian xác minh sẽ giảm đáng kể, tốc độ giao diện sẽ nhanh hơn rõ rệt.
Trang này có thể chứa nội dung của bên thứ ba, được cung cấp chỉ nhằm mục đích thông tin (không phải là tuyên bố/bảo đảm) và không được coi là sự chứng thực cho quan điểm của Gate hoặc là lời khuyên về tài chính hoặc chuyên môn. Xem Tuyên bố từ chối trách nhiệm để biết chi tiết.
Công nghệ SIWE hỗ trợ Dapp nâng cao xác thực danh tính người dùng
SIWE: Công nghệ then chốt nâng cao chức năng Dapp
SIWE (Đăng nhập bằng Ethereum) là một phương pháp xác thực danh tính người dùng trên Ethereum, tương tự như việc khởi động giao dịch ví, chứng minh người dùng có quyền kiểm soát ví. Phương pháp xác thực hiện tại rất đơn giản, chỉ cần ký thông tin trong plugin ví, hầu hết các plugin ví phổ biến đều đã hỗ trợ.
Bài viết này chủ yếu thảo luận về các tình huống ký trên Ethereum, không đề cập đến các chuỗi blockchain khác như Solana, SUI, v.v.
Có cần SIWE không
Nếu Dapp của bạn có các yêu cầu sau, hãy xem xét việc sử dụng SIWE:
Nhưng nếu Dapp của bạn chủ yếu là chức năng tra cứu, chẳng hạn như ứng dụng giống như etherscan, bạn có thể không sử dụng SIWE.
Bạn có thể thắc mắc, sau khi kết nối ví trên Dapp, chẳng phải đã chứng minh quyền sở hữu ví rồi sao? Câu này đúng một phần. Đối với phía trước, kết nối ví thực sự cho thấy danh tính, nhưng đối với các gọi API cần hỗ trợ phía sau, chỉ truyền địa chỉ là không đủ, vì địa chỉ là thông tin công khai, bất kỳ ai cũng có thể "mượn".
Nguyên lý và quy trình của SIWE
Quy trình SIWE có thể được tóm tắt thành ba bước: Kết nối ví - Ký tên - Nhận danh tính. Hãy cùng tìm hiểu chi tiết về ba bước này.
Kết nối ví
Kết nối ví là thao tác Web3 phổ biến, thông qua plugin ví có thể kết nối ví trong Dapp.
chữ ký
Các bước ký trong SIWE bao gồm lấy giá trị Nonce, ký ví và xác minh chữ ký từ phía máy chủ.
Để lấy giá trị Nonce, cần gọi API backend. Sau khi backend nhận được yêu cầu, nó sẽ tạo ra giá trị Nonce ngẫu nhiên và liên kết với địa chỉ hiện tại, chuẩn bị cho việc ký sau này.
Sau khi nhận được giá trị Nonce từ phía trước, cần xây dựng nội dung ký, bao gồm giá trị Nonce, tên miền, ID chuỗi, nội dung ký, v.v., thường sử dụng phương pháp ký do ví cung cấp.
Sau khi xây dựng xong chữ ký, hãy gửi nó cho backend.
Lấy danh tính
Sau khi kiểm tra chữ ký ở backend thành công, sẽ trả về danh tính người dùng, chẳng hạn như JWT. Trong các yêu cầu tiếp theo từ frontend, mang theo địa chỉ tương ứng và danh tính, có thể chứng minh quyền sở hữu ví.
Thực hành
Hiện có nhiều thành phần và thư viện hỗ trợ kết nối ví và SIWE nhanh chóng. Mục tiêu của chúng tôi là cho phép Dapp trả về JWT để xác thực danh tính người dùng. Lưu ý, bản demo này chỉ dùng để giới thiệu quy trình cơ bản của SIWE, việc sử dụng trong môi trường sản xuất có thể gặp vấn đề về bảo mật.
công việc chuẩn bị
Bài viết này sử dụng Next.js để phát triển ứng dụng, cần có môi trường Node.js. Lợi ích của việc sử dụng Next.js là có thể phát triển dự án full-stack trực tiếp, không cần phải chia thành hai dự án frontend và backend.
Cài đặt phụ thuộc
Đầu tiên cài đặt Next.js, thực hiện trong thư mục dự án:
npx create-next-app@14
Sau khi cài đặt hoàn tất theo hướng dẫn, vào thư mục dự án và chạy:
npm run dev
Theo hướng dẫn của terminal, bạn có thể truy cập localhost:3000 để xem dự án Next.js cơ bản.
Cài đặt các phụ thuộc liên quan đến SIWE
SIWE cần hệ thống đăng nhập, vì vậy cần kết nối ví. Ở đây chúng tôi sử dụng Ant Design Web3, vì:
Thực hiện tại terminal:
npm install antd @ant-design/web3 @ant-design/web3-wagmi wagmi viem @tanstack/react-query --save
Giới thiệu Wagmi
Ant Design Web3 của SIWE phụ thuộc vào thư viện Wagmi để thực hiện. Chúng ta cần nhập các Provider liên quan vào layout.tsx, để toàn bộ dự án có thể sử dụng các Hooks do Wagmi cung cấp.
Đầu tiên định nghĩa cấu hình WagmiProvider:
javascript "use client"; import { getNonce, verifyMessage } from "@/app/api"; import { Mainnet, MetaMask, OkxWallet, TokenPocket, WagmiWeb3ConfigProvider, WalletConnect, } từ "@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(địa chỉ)).dữ liệu, 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, }} ví={[ MetaMask(), WalletConnect(), TokenPocket({ nhóm: "Phổ biến", }), OkxWallet(), ]} queryClient={queryClient} > {children} ); };
xuất khẩu mặc định WagmiProvider;
Sau đó, thêm nút kết nối ví, như vậy đã thêm được điểm truy cập kết nối ở phía trước. Đến đây đã kết nối SIWE, bước thực hiện rất đơn giản.
Tiếp theo định nghĩa nút kết nối, thực hiện kết nối ví và ký.
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";
xuất khẩu chức năng mặc định App() { const jwt = React.useContext(JwtProvider);
const renderSignBtnText = ( defaultDom: React.ReactNode, tài khoản?: Tài khoản ) => { const { address } = account ?? {}; const ellipsisAddress = address ? ${address.slice(0, 6)}...${address.slice(-6)} : ""; đăng nhập dưới dạng ${ellipsisAddress}; };
return ( <>
Điều này đã tạo ra một khung đăng nhập SIWE đơn giản nhất.
triển khai giao diện
SIWE cần một số giao diện để giúp backend xác minh danh tính người dùng. Bây giờ chúng ta sẽ thực hiện một cách đơn giản.
Nonce
Nonce được sử dụng để thay đổi nội dung được tạo ra mỗi khi ví ký, tăng cường độ tin cậy của chữ ký. Việc tạo Nonce cần liên kết với địa chỉ do người dùng cung cấp, nhằm nâng cao độ chính xác của việc xác thực.
Nonce được triển khai rất đơn giản, đầu tiên tạo ra một chuỗi ngẫu nhiên ( được cấu thành từ chữ cái và số ), sau đó liên kết nonce với địa chỉ:
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");
nếu (!address) { throw new Error("Địa chỉ không hợp lệ"); } const nonce = randomBytes(16).toString("hex"); addressMap.set(address, nonce); return Response.json({ dữ liệu: nonce, }); }
signMessage
signMessage được sử dụng để ký nội dung, phần này thường được thực hiện bởi plugin ví, chúng tôi thường không cần cấu hình, chỉ cần chỉ định phương pháp là đủ. Trong demo này, chúng tôi sử dụng phương pháp ký của Wagmi.
xác minhTinNhắn
Sau khi người dùng ký, cần gửi nội dung trước khi ký và chữ ký cùng nhau cho backend để xác thực. Backend sẽ giải mã nội dung tương ứng từ chữ ký để so sánh, nếu一致 thì表示 xác thực thành công.
Ngoài ra, cần phải kiểm tra tính an toàn của nội dung chữ ký, chẳng hạn như giá trị Nonce trong nội dung chữ ký có khớp với giá trị đã phát cho người dùng hay không. Sau khi xác minh thành công, cần trả về JWT của người dùng để sử dụng cho việc kiểm tra quyền sau này, mã mẫu như sau:
javascript import { createPublicClient, http } from "viem"; import { mainnet } từ "viem/chains"; import jwt từ "jsonwebtoken"; import { parseSiweMessage } from "viem/siwe"; import { addressMap } from "../cache";
const JWT_SECRET = "your-secret-key"; // Vui lòng sử dụng khóa an toàn hơn và thêm kiểm tra hết hạn tương ứng.
const publicClient = createPublicClient({ chuỗi: mainnet, vận chuyển: http(), });
xuất async function POST(request: Request) { const { signature, message } = await request.json();
const { nonce, address = "0x" } = parseSiweMessage(message); console.log("nonce", nonce, address, addressMap);
// Kiểm tra xem giá trị nonce có khớp không nếu (!nonce || nonce !== addressMap.get(address)) { throw new Error("Nonce không hợp lệ"); }
// Kiểm tra nội dung chữ ký const valid = await publicClient.verifySiweMessage({ thông điệp, địa chỉ, chữ ký, });
nếu (!valid) { throw new Error("Chữ ký không hợp lệ"); }
// Tạo jwt và trả về const token = jwt.sign({ address }, JWT_SECRET, { expiresIn: "1h" }); return Response.json({ dữ liệu: token, }); }
Đến đây, một Dapp cơ bản để thực hiện đăng nhập SIWE đã hoàn thành.
Đề xuất tối ưu hóa
Việc xác thực đăng nhập SIWE bằng cách sử dụng nút RPC mặc định có thể mất gần 30 giây, vì vậy rất khuyến nghị sử dụng dịch vụ nút chuyên dụng để tăng tốc độ phản hồi của giao diện. Bài viết này sử dụng dịch vụ nút của ZAN, có thể lấy kết nối RPC tương ứng từ bảng điều khiển dịch vụ nút ZAN.
Sau khi nhận được kết nối HTTPS RPC của mạng chính Ethereum, hãy thay thế RPC mặc định của publicClient trong mã.
javascript const publicClient = createPublicClient({ chuỗi: mainnet, vận chuyển: http('), // dịch vụ RPC của nút ZAN nhận được });
Sau khi thay thế, thời gian xác minh sẽ giảm đáng kể, tốc độ giao diện sẽ nhanh hơn rõ rệt.