WEB3全棧開發——面試專業技能點P6后端框架 / 微服務設計

一、Express

Express是國內大部分公司重點問的。我在本文最后,單獨講解了Express框架。

概念介紹

Express 是基于 Node.js 平臺的極簡、靈活且廣泛使用的 Web 應用框架。它提供了一系列強大的功能,用于構建單頁、多頁及混合型的 Web 應用程序和 API 服務。

Express 的核心特點包括:

  • 簡潔易用的路由系統

  • 中間件機制,方便請求處理和功能擴展

  • 靈活的模板引擎支持

  • 支持多種 HTTP 請求方法和路徑匹配

  • 兼容性強,易于與各種數據庫和前端框架集成

Express 通常被用作后端 Web 服務的骨架,尤其在構建 RESTful API 和微服務架構時非常流行。

示例代碼

下面是一個簡單的 Express 服務器示例,實現一個基礎的 GET 請求接口:

const express = require('express');
const app = express();
const port = 3000;// 定義路由,處理 GET 請求
app.get('/', (req, res) => {res.send('Hello, Express!');
});// 啟動服務器監聽端口
app.listen(port, () => {console.log(`Express server listening at http://localhost:${port}`);
});

講解總結

  • 路由管理:Express 通過 app.get, app.post 等方法定義 URL 路徑對應的請求處理函數,支持參數、查詢字符串等。

  • 中間件機制:Express 支持中間件函數,可以攔截請求,實現功能如身份驗證、日志記錄、請求體解析等。

  • 簡潔高效:相比原生 Node.js HTTP 模塊,Express 大幅簡化了代碼量和復雜度,提升開發效率。

  • 靈活擴展:擁有龐大的生態系統,可集成多種第三方中間件與插件,滿足各種業務需求。

  • 廣泛應用:常用于構建 RESTful API、前后端分離架構的后端服務,及微服務組件。

Express 是 Node.js Web 開發的基礎框架,掌握它對后端開發非常關鍵。

二、Koa

概念介紹

Koa 是由 Express 原班人馬開發的下一代 Node.js Web 框架,設計目標是打造一個更小、更富表現力、更健壯的基礎框架。它利用現代 JavaScript 的 async/await 特性,簡化異步流程控制,摒棄了傳統中間件的回調嵌套問題,提升代碼的可讀性和維護性。

Koa 本身非常輕量,不內置中間件,開發者可以根據需要自由組合,具有極高的靈活性。


示例代碼

下面是一個使用 Koa 創建的簡單服務器,響應 GET 請求:

const Koa = require('koa');
const app = new Koa();
const port = 3000;// 定義中間件,處理請求
app.use(async (ctx) => {if (ctx.path === '/') {ctx.body = 'Hello, Koa!';} else {ctx.status = 404;ctx.body = 'Not Found';}
});// 啟動服務器監聽端口
app.listen(port, () => {console.log(`Koa server running at http://localhost:${port}`);
});

講解總結

  • 現代異步處理:Koa 使用 async/await 處理異步代碼,避免回調地獄,使代碼更簡潔易懂。

  • 洋蔥模型中間件:中間件執行遵循洋蔥模型(洋蔥圈層),支持在請求進入和響應返回時進行處理,便于實現日志、錯誤捕獲、響應壓縮等功能。

  • 極簡核心:Koa 只提供核心功能,不包含路由、中間件等,開發者可根據業務需求靈活引入,打造定制化架構。

  • 更好錯誤處理:通過 async 函數的錯誤捕獲機制,Koa 能優雅地處理異步錯誤,提升程序穩定性。

  • 適合微服務:Koa 的靈活性和簡潔性非常適合用來構建輕量級的微服務或 API 服務。

Koa 是 Node.js 生態中注重現代語法與靈活設計的 Web 框架,適合對代碼質量和擴展性有較高要求的項目。

三、NestJS 的模塊化架構

概念介紹

NestJS 是一個基于 TypeScript 構建的進階 Node.js 框架,借鑒了 Angular 的設計理念,采用模塊化架構來組織應用。模塊(Module)是 NestJS 應用的基本組成單元,每個模塊封裝了一組相關的功能,包括控制器(Controllers)、服務(Providers)、導入的其他模塊等。

模塊化架構有助于分離關注點,提升代碼的復用性和可維護性,使大型應用易于管理和擴展。


示例代碼

下面是一個簡單的模塊定義示例,展示如何創建和使用模塊:

import { Module, Injectable, Controller, Get } from '@nestjs/common';// 服務層,提供業務邏輯
@Injectable()
export class HelloService {getHello(): string {return 'Hello, NestJS Module!';}
}// 控制器層,處理請求
@Controller()
export class HelloController {constructor(private readonly helloService: HelloService) {}@Get()getHello(): string {return this.helloService.getHello();}
}// 定義模塊,組織控制器和服務
@Module({imports: [],            // 導入其他模塊controllers: [HelloController],providers: [HelloService],exports: [HelloService], // 可導出給其他模塊使用
})
export class HelloModule {}

講解總結

  • 模塊(Module) 是 NestJS 應用的組織單位,使用 @Module 裝飾器定義,包含控制器、服務和導入的模塊。

  • 控制器(Controller) 負責處理客戶端請求,定義路由和請求方法。

  • 服務(Provider) 封裝業務邏輯,支持依賴注入(DI),解耦業務與控制層。

  • 模塊之間通過導入(imports)和導出(exports)實現功能復用和共享,方便拆分大型應用為多個獨立子模塊。

  • 模塊化架構提高應用可維護性和擴展性,便于團隊協作和功能拆分。

  • NestJS 的模塊設計結合了依賴注入和面向對象編程思想,令開發體驗更現代化且高效。

掌握 NestJS 的模塊化架構是構建清晰、結構良好的企業級應用的基礎。

四、NestJS的依賴注入

概念介紹

依賴注入(Dependency Injection,簡稱 DI)是一種設計模式,通過將對象的依賴(例如服務)由框架自動提供,而不是由對象自行創建,從而實現代碼解耦和模塊間松耦合。

NestJS 內置強大的依賴注入容器,自動管理服務實例的創建和生命周期,使組件之間的依賴關系清晰且易于維護。通過構造函數注入(constructor injection)是 NestJS DI 的核心方式。


示例代碼

下面示例展示如何在 NestJS 中通過依賴注入使用服務:

import { Injectable, Controller, Get } from '@nestjs/common';// 定義服務,提供業務邏輯
@Injectable()
export class UserService {getUser() {return { id: 1, name: 'Alice' };}
}// 定義控制器,依賴注入 UserService
@Controller('users')
export class UserController {constructor(private readonly userService: UserService) {}@Get()getUser() {return this.userService.getUser();}
}

講解總結

  • @Injectable() 裝飾器標記服務類,使其可以被 NestJS 容器管理和注入。

  • 構造函數注入:依賴通過控制器或其他服務的構造函數參數聲明,NestJS 自動實例化并傳入對應依賴。

  • 依賴注入容器會根據作用域(默認單例)管理服務實例,避免重復創建,提高性能。

  • 依賴注入解耦了類與其依賴,實現高內聚低耦合,有利于單元測試和代碼維護。

  • NestJS 還支持自定義作用域(如請求作用域)和手動注入(通過 @Inject() 裝飾器),增強靈活性。

依賴注入是 NestJS 核心設計之一,掌握它可以大幅提升項目結構的清晰度和擴展性。

五、NestJS的守衛

概念介紹

守衛(Guard)是 NestJS 中用于控制請求權限的機制,類似于中間件,但專注于授權和權限檢查。守衛可以決定請求是否可以繼續執行路由處理邏輯,通常用于身份驗證、角色權限校驗等場景。

守衛實現 CanActivate 接口,返回 true 允許請求繼續,返回 false 或拋出異常則拒絕請求。


示例代碼

下面示例展示一個簡單的守衛,用于檢查請求頭中是否包含特定令牌:

import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { Observable } from 'rxjs';@Injectable()
export class AuthGuard implements CanActivate {canActivate(context: ExecutionContext,): boolean | Promise<boolean> | Observable<boolean> {const request = context.switchToHttp().getRequest();const token = request.headers['authorization'];// 簡單校驗:請求頭必須有指定的 tokenreturn token === 'my-secret-token';}
}

在控制器中使用守衛:

import { Controller, Get, UseGuards } from '@nestjs/common';@Controller('profile')
@UseGuards(AuthGuard)  // 作用于整個控制器
export class ProfileController {@Get()getProfile() {return { name: 'Alice', role: 'admin' };}
}

講解總結

  • 守衛通過實現 CanActivate 接口控制請求是否被處理,適合做權限、認證邏輯。

  • 通過 ExecutionContext 獲取請求信息(如請求頭、用戶信息等)。

  • 守衛返回 true 允許請求繼續,返回 false 或拋異常拒絕請求。

  • 守衛可以作用于控制器類或單個路由方法,支持靈活配置。

  • NestJS 結合守衛和中間件、攔截器等機制,實現強大的請求生命周期管理。

掌握守衛可幫助構建安全、可控的后端服務,確保敏感接口僅授權用戶訪問。

六、NestJS 的攔截器

概念介紹

攔截器(Interceptor)是 NestJS 中一種強大的功能,用于攔截和處理函數調用前后邏輯。攔截器可以用于:

  • 修改方法輸入參數或返回結果

  • 實現日志記錄、緩存、異常處理、性能監控

  • 對請求進行額外處理或響應包裝

攔截器實現 NestInterceptor 接口,核心方法 intercept() 接收 ExecutionContextCallHandler,通過 RxJS 操作符處理請求流。


示例代碼

下面示例是一個簡單的日志攔截器,打印請求開始和結束時間:

import {Injectable,NestInterceptor,ExecutionContext,CallHandler,
} from '@nestjs/common';
import { Observable, tap } from 'rxjs';@Injectable()
export class LoggingInterceptor implements NestInterceptor {intercept(context: ExecutionContext, next: CallHandler): Observable<any> {console.log('Before handling request...');const now = Date.now();return next.handle().pipe(tap(() => console.log(`After handling request... ${Date.now() - now}ms`)),);}
}

使用攔截器:

import { Controller, Get, UseInterceptors } from '@nestjs/common';@Controller('items')
@UseInterceptors(LoggingInterceptor)
export class ItemsController {@Get()findAll() {return ['item1', 'item2'];}
}

講解總結

  • 攔截器在請求處理前后執行,能夠操作請求和響應數據流。

  • 通過 RxJS 操作符(如 tapmap)可以異步處理響應。

  • 攔截器廣泛應用于日志、緩存、異常轉換、數據格式化等場景。

  • 可以作用于全局、控制器、單個路由,支持靈活配置。

  • 結合守衛和管道,構成 NestJS 完整請求處理鏈。

掌握攔截器能夠極大增強應用的功能擴展性和代碼復用性。

七、Express 的模塊化架構

概念介紹

Express 默認是一個輕量級的 Node.js Web 框架,支持快速搭建服務器和路由。模塊化架構指的是將應用拆分為多個功能模塊,每個模塊獨立管理路由、控制器和中間件,便于代碼維護、復用和團隊協作。

核心思想:

  • 路由拆分:每個模塊有自己獨立路由文件,負責特定業務路由。

  • 控制器分離:處理業務邏輯的函數單獨放置,保持路由簡潔。

  • 中間件復用:公共功能用中間件抽象,跨模塊復用。

  • 按功能組織代碼:目錄結構清晰,易于擴展。

模塊化架構讓大型項目更易維護,同時也方便測試和協作。


示例代碼

假設一個簡單的用戶模塊和商品模塊,拆分路由和控制器。

目錄結構示例
/app/controllersuserController.jsproductController.js/routesuserRoutes.jsproductRoutes.jsapp.js                   // app.js 中掛載路由前綴(主入口)

userController.js
// 處理用戶相關業務邏輯
// controllers/userController.js
exports.getUser = (req, res) => {const userId = req.params.id;// 模擬獲取用戶信息res.json({ id: userId, name: 'Alice' });
};
productController.js
// 處理商品相關業務邏輯
exports.getProduct = (req, res) => {const productId = req.params.id;// 模擬獲取商品信息res.json({ id: productId, name: 'Phone', price: 599 });
};
userRoutes.js(在app.js中綁定了路徑前綴 /users
const express = require('express');
const router = express.Router();
const userController = require('../controllers/userController');
// 實際請求 URL: GET /users/:id
router.get('/:id', userController.getUser);module.exports = router;
productRoutes.js(在app.js中綁定了路徑前綴 /products
const express = require('express');
const router = express.Router();
const productController = require('../controllers/productController');// 實際請求 URL: GET /products/:id
router.get('/:id', productController.getProduct);module.exports = router;
app.js中掛載路由(主入口)
const express = require('express');
const app = express();const userRoutes = require('./routes/userRoutes');
const productRoutes = require('./routes/productRoutes');// 路由前綴綁定
app.use('/users', userRoutes);    // 所有 user 路由以 /users 開頭
app.use('/products', productRoutes);    // 所有 product 路由以 /products 開頭app.listen(3000, () => {console.log('Server running on port 3000');
});

實際完整 URL 路徑

結合以上配置:

功能請求方法完整 URL 示例控制器函數
獲取用戶GEThttp://localhost:3000/users/123getUser()
獲取商品GEThttp://localhost:3000/products/456getProduct()

如果你還想加 POST、PUT、DELETE 這類方法,也可以在路由里擴展,例如:

router.post('/', userController.createUser);  // POST /users

講解總結

  • 職責分明:路由只負責請求分發,業務邏輯放在控制器,代碼層次清晰。

  • 易于維護:模塊化結構使代碼易讀,方便多人協作和后續功能擴展。

  • 復用性強:公共中間件可跨模塊使用,提高代碼復用率。

  • 便于測試:模塊化讓單元測試和集成測試更加簡便。

Express 模塊化架構適合中大型項目,是構建可維護、擴展性好的 Node.js 應用的推薦方式。

八、Express 的依賴注入

? 概念介紹:Express 的依賴注入

Express 本身并不內建依賴注入機制,它是一個極簡主義框架。與 NestJS 不同,NestJS 是基于 Angular 風格的完整依賴注入系統構建的。但在 Express 中,你可以使用一些第三方庫(如 awilix、inversify 等)手動實現依賴注入,來提升項目的模塊化與可測試性。

依賴注入(DI)的目標是:將對象之間的依賴關系“注入”而非硬編碼,讓代碼更解耦、更好測試、更易維護。


? 示例代碼(使用 awilix 實現 Express 的依賴注入)

1. 安裝依賴

npm install awilix awilix-express

2. 項目結構

app/
├── app.js
├── routes/
│   └── userRoutes.js
├── controllers/
│   └── userController.js
├── services/
│   └── userService.js
└── container.js

3. 創建服務層(業務邏輯)

// services/userService.js
class UserService {getUser(id) {return { id, name: 'Alice (DI)' };}
}module.exports = UserService;

4. 創建控制器(接收依賴)

// controllers/userController.js
class UserController {constructor({ userService }) {this.userService = userService;}getUser = (req, res) => {const user = this.userService.getUser(req.params.id);res.json(user);};
}module.exports = UserController;

5. 設置 Awilix 容器

// container.js
const { createContainer, asClass } = require('awilix');
const UserService = require('./services/userService');
const UserController = require('./controllers/userController');const container = createContainer();container.register({userService: asClass(UserService).scoped(),userController: asClass(UserController).scoped(),
});module.exports = container;

6. 路由綁定(通過 awilix-express)

// routes/userRoutes.js
const express = require('express');
const router = express.Router();
const { makeInvoker } = require('awilix-express');// 控制器加載器
const container = require('../container');
const userController = makeInvoker(container.resolve('userController'));router.get('/:id', userController('getUser'));module.exports = router;

7. 應用入口(掛載路由)

// app.js
const express = require('express');
const { scopePerRequest } = require('awilix-express');
const container = require('./container');
const userRoutes = require('./routes/userRoutes');const app = express();
app.use(scopePerRequest(container)); // 關鍵 DI 掛載點
app.use('/users', userRoutes);app.listen(3000, () => {console.log('Server running on http://localhost:3000');
});

? 總結

項目結構描述
userService.js提供獨立服務邏輯,可復用
userController.js通過構造函數自動注入依賴
container.js中央依賴注入容器
awilix-express把 DI 自動接入 Express 生命周期中


是否需要我補充 inversify 版本、或者如何結合 JWT數據庫服務 等依賴的注入結構?

九、Express的守衛

? 概念介紹:Express 的守衛(Guard)

在 NestJS 中,“守衛”(Guard)是用來控制某個請求是否有權限訪問的類。但在 Express 中沒有“守衛”這一專有概念,不過你可以用 中間件(Middleware) 實現類似“守衛”的功能。

Express 中的“守衛”常用于:

  • 登錄校驗(是否帶有 Token)

  • 權限校驗(是否管理員)

  • 請求頻率限制、接口黑白名單控制等


? 示例代碼:實現 Express 中的“守衛”功能

🎯 目標:實現一個 JWT 鑒權“守衛”

我們將創建一個中間件,驗證請求是否攜帶合法的 JWT Token。


1. 安裝依賴

npm install jsonwebtoken

2. 編寫守衛中間件(authGuard.js

// middlewares/authGuard.js
const jwt = require('jsonwebtoken');
const SECRET = 'your_secret_key'; // 應放在 .env 環境變量中const authGuard = (req, res, next) => {const authHeader = req.headers['authorization'];if (!authHeader || !authHeader.startsWith('Bearer ')) {return res.status(401).json({ message: '未提供有效 Token' });}const token = authHeader.split(' ')[1];try {const payload = jwt.verify(token, SECRET);req.user = payload; // 可用于后續控制器中next(); // 放行} catch (err) {return res.status(403).json({ message: 'Token 無效或已過期' });}
};module.exports = authGuard;

3. 使用守衛中間件保護路由

// routes/userRoutes.js
const express = require('express');
const router = express.Router();
const authGuard = require('../middlewares/authGuard');router.get('/profile', authGuard, (req, res) => {// 只有驗證通過的用戶才能訪問res.json({ message: `歡迎你,${req.user.username}` });
});module.exports = router;

4. 登錄接口生成 Token 示例

// routes/auth.js
const express = require('express');
const jwt = require('jsonwebtoken');
const router = express.Router();router.post('/login', (req, res) => {const { username, password } = req.body;// 真實項目應校驗數據庫if (username === 'admin' && password === '123456') {const token = jwt.sign({ username, role: 'admin' }, 'your_secret_key', { expiresIn: '2h' });return res.json({ token });}res.status(401).json({ message: '賬號或密碼錯誤' });
});module.exports = router;

? 請求示例

1. 登錄獲取 Token

POST /login
Content-Type: application/json{"username": "admin","password": "123456"
}

響應:

{"token": "eyJhbGciOi..."
}

2. 訪問受保護資源

GET /users/profile
Authorization: Bearer eyJhbGciOi...

? 總結

名稱實現方式
守衛(Nest)使用 @Injectable()
守衛(Express)使用中間件函數(req, res, next
應用場景JWT、權限控制、接口限流、黑白名單等


如果你想實現角色權限守衛、API 接口簽名驗證等高級“守衛”,我也可以繼續幫你寫完整示例。是否需要?

十、Express 攔截器

? 概念介紹:Express 的攔截器(Interceptor)

在 NestJS 中,“攔截器”是一個強大的功能,用于擴展請求/響應行為(如統一響應格式、日志記錄、異常包裝等)。
Express 雖沒有原生“攔截器”這個名詞,但我們可以通過 中間件(Middleware) 實現“攔截器”功能。

? 一句話理解:在 Express 中,“攔截器”是一個特定用途的中間件,用來在請求進入控制器之前/之后進行邏輯處理。


? 常見用途:

  • 請求/響應日志記錄

  • 請求耗時分析

  • 接口統一響應格式處理

  • 異常捕獲與封裝

  • 跨域處理


? 示例代碼

🎯 示例:編寫一個記錄請求時間和統一響應格式的攔截器中間件


1. 日志與響應包裝攔截器 interceptor.js

// middlewares/interceptor.js
module.exports = (req, res, next) => {const startTime = Date.now();// 重寫 res.json 方法,實現統一結構const originalJson = res.json.bind(res);res.json = (data) => {const duration = Date.now() - startTime;return originalJson({code: 0,message: 'success',data,duration: `${duration}ms`});};next();
};

2. 應用攔截器中間件到 Express 應用

// app.js
const express = require('express');
const app = express();
const interceptor = require('./middlewares/interceptor');app.use(express.json());
app.use(interceptor); // 全局攔截器app.get('/api/hello', (req, res) => {res.json({ text: 'Hello World!' });
});app.listen(3000, () => {console.log('Server running on http://localhost:3000');
});

🧪 請求示例

GET /api/hello

💡 響應結果(統一格式):

{"code": 0,"message": "success","data": {"text": "Hello World!"},"duration": "2ms"
}

? 擴展用法:僅攔截特定路由

app.use('/api/secure', interceptor);

? 總結

功能Express 實現方式
攔截器(請求 & 響應)中間件函數包裹 res.jsonres.send
執行順序注冊順序決定調用鏈,越早注冊越早執行
特點可用作全局或局部中間件


如需實現 異常處理攔截器權限校驗攔截器鏈上接口統一響應結構 等,我也可以提供對應示例。需要的話告訴我即可。

十一、Express 的 JWT 設計鏈上鏈下鑒權系統

概念介紹

在 Web3 應用中,鏈上身份驗證通常依賴區塊鏈錢包簽名消息(如 MetaMask 簽名),而鏈下服務(如后端 API)使用 JWT(JSON Web Token)維護會話狀態,實現權限控制。鏈上鏈下鑒權系統結合了這兩者:

  • 用戶通過錢包簽名證明身份(鏈上認證)

  • 服務器驗證簽名后簽發 JWT,用于后續鏈下請求鑒權

  • JWT 包含用戶地址等信息,攜帶在請求頭,服務器驗證后允許訪問受保護資源

這種設計避免每次請求都要求錢包簽名,提高用戶體驗,同時保持安全性。


示例代碼

以下示例用 Express 和 jsonwebtoken 實現簡易鏈上鏈下鑒權流程:

const express = require('express');
const jwt = require('jsonwebtoken');
const { ethers } = require('ethers');const app = express();
app.use(express.json());const JWT_SECRET = 'your_jwt_secret';// 生成隨機消息供客戶端簽名
app.get('/auth/message/:address', (req, res) => {const { address } = req.params;const message = `Login to MyDApp at ${Date.now()}`;// 這里應緩存 message 與 address 對應,用于驗證res.json({ message });
});// 驗證簽名并簽發 JWT
app.post('/auth/verify', (req, res) => {const { address, signature, message } = req.body;try {// 使用 ethers 驗證簽名者地址const signerAddress = ethers.utils.verifyMessage(message, signature);if (signerAddress.toLowerCase() !== address.toLowerCase()) {return res.status(401).json({ error: 'Invalid signature' });}// 簽名合法,簽發 JWTconst token = jwt.sign({ address }, JWT_SECRET, { expiresIn: '1h' });res.json({ token });} catch (error) {res.status(400).json({ error: 'Verification failed' });}
});// 受保護接口,驗證 JWT
function authenticateToken(req, res, next) {const authHeader = req.headers['authorization'];const token = authHeader && authHeader.split(' ')[1];if (!token) return res.sendStatus(401);jwt.verify(token, JWT_SECRET, (err, user) => {if (err) return res.sendStatus(403);req.user = user; // 保存解碼后的用戶信息next();});
}app.get('/protected', authenticateToken, (req, res) => {res.json({ message: `Hello ${req.user.address}, this is protected data.` });
});app.listen(3000, () => {console.log('Server started on port 3000');
});

講解總結

  • 鏈上認證:用戶通過錢包簽名服務器發送的隨機消息,證明對該地址的控制權。

  • 鏈下鑒權:服務器驗證簽名后,使用 JWT 生成包含用戶地址的令牌,客戶端持有此令牌訪問受保護接口。

  • JWT 驗證:服務器中間件檢查請求中的 JWT,保證請求合法且未過期。

  • 優勢:減少頻繁簽名操作,提升用戶體驗;同時保證安全與身份唯一性。

這種模式是典型的 Web3 應用鑒權方案,兼顧區塊鏈身份驗證與傳統后端權限控制。

十二、Express 的錢包簽名(MetaMask)設計鏈上鏈下鑒權系統

概念介紹

在 Web3 應用中,用戶使用錢包(如 MetaMask)進行鏈上身份認證。通過錢包簽名服務器隨機生成的消息(challenge),證明其對某個以太坊地址的控制權。服務器驗證簽名后,生成鏈下的 JWT 令牌,用戶憑此令牌訪問后端受保護資源。

核心流程

  1. 服務器生成隨機消息(challenge)并發給客戶端。

  2. 客戶端用 MetaMask 連接錢包,簽名該消息。

  3. 客戶端將簽名與地址發回服務器。

  4. 服務器驗證簽名,確認用戶身份后,頒發 JWT。

  5. 后續請求攜帶 JWT 進行鏈下身份驗證。

該方案結合鏈上簽名的安全性和鏈下 JWT 的高效性,實現用戶友好且安全的認證授權。


示例代碼

const express = require('express');
const jwt = require('jsonwebtoken');
const { ethers } = require('ethers');const app = express();
app.use(express.json());const JWT_SECRET = 'your_jwt_secret';// 簡單內存存儲,實際項目應用數據庫或緩存
const challenges = {};// 1. 客戶端請求獲取挑戰消息
app.get('/auth/challenge/:address', (req, res) => {const { address } = req.params;const challenge = `登錄驗證消息:${Date.now()}`;challenges[address.toLowerCase()] = challenge;res.json({ challenge });
});// 2. 客戶端提交簽名和地址進行驗證
app.post('/auth/verify', (req, res) => {const { address, signature } = req.body;const challenge = challenges[address.toLowerCase()];if (!challenge) {return res.status(400).json({ error: 'Challenge not found' });}try {// 驗證簽名是否匹配地址const signer = ethers.utils.verifyMessage(challenge, signature);if (signer.toLowerCase() !== address.toLowerCase()) {return res.status(401).json({ error: '簽名驗證失敗' });}// 驗證成功,簽發 JWT,1 小時過期const token = jwt.sign({ address }, JWT_SECRET, { expiresIn: '1h' });// 可刪除已使用的挑戰,防止重放攻擊delete challenges[address.toLowerCase()];res.json({ token });} catch (error) {res.status(400).json({ error: '簽名驗證異常' });}
});// 3. JWT 驗證中間件
function authenticateToken(req, res, next) {const authHeader = req.headers['authorization'];if (!authHeader) return res.sendStatus(401);const token = authHeader.split(' ')[1];if (!token) return res.sendStatus(401);jwt.verify(token, JWT_SECRET, (err, user) => {if (err) return res.sendStatus(403);req.user = user;next();});
}// 4. 受保護資源示例
app.get('/protected', authenticateToken, (req, res) => {res.json({ message: `歡迎 ${req.user.address},訪問受保護資源成功。` });
});app.listen(3000, () => {console.log('服務器運行于端口 3000');
});

講解總結

  • 挑戰消息(challenge):防止重放攻擊,確保每次認證的唯一性。

  • 錢包簽名:客戶端用 MetaMask 調用 eth_signpersonal_sign 簽名 challenge,證明地址所有權。

  • 簽名驗證:服務器用 ethers.utils.verifyMessage 驗證簽名對應的地址是否正確。

  • JWT 令牌:簽名驗證通過后,服務器生成 JWT,客戶端持有該令牌訪問后端資源,無需每次都簽名。

  • 安全防護:使用 challenge 階段限制重放,JWT 過期和服務器驗證保護接口安全。

這種設計模式是當前主流 Web3 應用鏈上鏈下鑒權方案,兼具安全性和使用便利。

十三、Express 構建 DApp 的后端微服務架構

概念介紹

DApp(去中心化應用)通常需要后端服務來處理鏈上數據索引、用戶身份管理、業務邏輯處理等。使用 Express 構建后端微服務架構,意味著將系統拆分成多個小型服務模塊,每個模塊專注單一職責,通過 API 接口相互通信,便于維護、擴展和獨立部署。

關鍵點:

  • 模塊化設計:每個微服務負責不同功能(如用戶認證、交易處理、事件監聽等)。

  • API 網關:統一入口,路由請求到不同微服務。

  • 異步消息隊列(如 RabbitMQ/Kafka)用于微服務間解耦和異步通信。

  • 鏈上鏈下數據分離:微服務可專注鏈上事件處理或鏈下數據存儲。

  • 使用 JWT 或錢包簽名做鑒權

  • Docker 容器化部署,支持彈性擴縮。


示例代碼

示例中展示一個簡單的微服務結構示意,用 Express 實現用戶服務和事件服務,并通過 HTTP 請求互相調用。

用戶服務 user-service.js
const express = require('express');
const app = express();
app.use(express.json());app.post('/login', (req, res) => {// 處理用戶登錄,返回 tokenres.json({ token: 'user-jwt-token' });
});app.get('/profile/:address', (req, res) => {const { address } = req.params;// 查詢用戶鏈下數據res.json({ address, name: 'Alice', assets: [] });
});app.listen(3001, () => {console.log('User service running on port 3001');
});
事件服務 event-service.js
const express = require('express');
const axios = require('axios');
const app = express();
app.use(express.json());app.post('/process-event', async (req, res) => {const eventData = req.body;// 處理鏈上事件邏輯,比如入庫、觸發業務等// 調用用戶服務示例:查詢用戶信息try {const response = await axios.get(`http://localhost:3001/profile/${eventData.userAddress}`);console.log('用戶信息', response.data);} catch (err) {console.error('調用用戶服務失敗', err);}res.json({ status: 'event processed' });
});app.listen(3002, () => {console.log('Event service running on port 3002');
});

講解總結

  • 職責分離:用戶身份管理與鏈上事件處理分別獨立微服務,互不影響,方便獨立維護和升級。

  • 服務間通信:使用 HTTP REST 調用(示例中用 axios),也可用消息隊列解耦。

  • 擴展性好:服務可以水平擴展、獨立部署,提高系統可用性和穩定性。

  • 安全性:每個微服務獨立實現鑒權機制,保護數據安全。

  • 鏈上數據處理:事件服務負責監聽鏈上事件,異步處理后寫入鏈下數據庫,優化響應速度。

  • 容器化與自動化部署:結合 Docker 和 Kubernetes 做微服務編排和管理。

Express 結合微服務架構,是構建高效、靈活的 Web3 后端服務的常見方案。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/86425.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/86425.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/86425.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

游戲開發中的CI/CD優化案例:知名游戲公司Gearbox使用TeamCity簡化CI/CD流程

案例背景 關于Gearbox&#xff1a; Gearbox 是一家美國電子游戲公司&#xff0c;總部位于德克薩斯州弗里斯科&#xff0c;靠近達拉斯。Gearbox 成立于1999年&#xff0c;推出過多款史上最具代表性的視頻游戲&#xff0c;包括《半衰期》、《戰火兄弟連》以及《無主之地》。 團隊…

視覺slam--三維剛體運動

線性代數 外積與矩陣乘法的等價性 歐拉角的奇異性--萬向死鎖 現象 第二個軸旋轉度&#xff0c;會導致第三個旋轉軸和惡原始坐標軸的第一個旋轉軸重合&#xff0c;導致第一次旋轉與第三次旋轉都使用了同一個軸進行旋轉&#xff0c;也就是本質上旋轉三次&#xff0c;但是只在兩個…

內窺鏡檢查中基于提示的息肉分割|文獻速遞-深度學習醫療AI最新文獻

Title 題目 Prompt-based polyp segmentation during endoscopy 內窺鏡檢查中基于提示的息肉分割 01 文獻速遞介紹 以下是對這段英文內容的中文翻譯&#xff1a; ### 胃腸道癌癥的發病率呈上升趨勢&#xff0c;且有年輕化傾向&#xff08;Bray等人&#xff0c;2018&#x…

CppCon 2015 學習:REFLECTION TECHNIQUES IN C++

關于 Reflection&#xff08;反射&#xff09; 這個概念&#xff0c;總結一下&#xff1a; Reflection&#xff08;反射&#xff09;是什么&#xff1f; 反射是對類型的自我檢查能力&#xff08;Introspection&#xff09; 可以查看類的成員變量、成員函數等信息。反射允許枚…

R語言速釋制劑QBD解決方案之一

本文是《Quality by Design for ANDAs: An Example for Immediate-Release Dosage Forms》第一個處方的R語言解決方案。 第一個處方研究評估原料藥粒徑分布、MCC/Lactose比例、崩解劑用量對制劑CQAs的影響。 第二處方研究用于理解顆粒外加硬脂酸鎂和滑石粉對片劑質量和可生產…

“詳規一張圖”——新加坡土地利用數據

在城市規劃和土地管理領域&#xff0c;精確且詳盡的空間數據是進行有效決策的基石。隨著地理信息系統&#xff08;GIS&#xff09;技術的發展&#xff0c;我們能夠以前所未有的精度和細節來捕捉、分析和展示土地利用信息。這不僅提升了數據的質量和可靠性&#xff0c;還使得城市…

LabVIEW雙光子成像系統技術

雙光子成像技術的核心特性 雙光子成像通過雙低能量光子協同激發機制&#xff0c;展現出顯著的技術優勢&#xff1a; 深層組織穿透能力&#xff1a;適用于活體組織深度成像 高分辨率觀測性能&#xff1a;滿足微觀結構的精細研究需求 低光毒性特點&#xff1a;減少對樣本的損傷…

MySQL自定義函數零基礎學習教程

1. 引言 想象一下&#xff0c;你在用計算器做數學題。每次計算"圓形面積"時&#xff0c;你都要輸入&#xff1a;3.14 半徑 半徑。如果能把這個計算步驟保存起來&#xff0c;下次只要輸入半徑就自動算出面積&#xff0c;那該多方便&#xff01; MySQL自定義函數就…

八股---7.JVM

1. JVM組成 1.1 JVM由哪些部分組成?運行流程? 難易程度:☆☆☆ 出現頻率:☆☆☆☆ Java Virtual Machine:Java 虛擬機,Java程序的運行環境(java二進制字節碼的運行環境)好處:一次編寫,到處運行;自動內存管理,垃圾回收機制程序運行之前,需要先通過編譯器將…

企業級AI-DevOps工具鏈的構成及實現方案

企業級AI-DevOps工具鏈的構成及實現方案 DevOps在AI大模型研發中的重要性及應用背景一、場景驅動的AI產品研發運營機制二、AI-DevOps生產線建設三、基于DevOps的AI大模型研發機制四、基于DevOps的智能體場景研發機制五、場景驅動的應用評估分析機制 DevOps在AI大模型研發中的重…

在 Spring Boot 項目里,MYSQL中json類型字段使用

前言&#xff1a; 因為程序特殊需求導致&#xff0c;需要mysql數據庫存儲json類型數據&#xff0c;因此記錄一下使用流程 1.java實體中新增字段 private List<User> users 2.增加mybatis-plus注解 TableField(typeHandler FastjsonTypeHandler.class) private Lis…

Python競賽環境搭建全攻略

Python環境搭建競賽技術文章大綱 競賽背景與意義 競賽的目的與價值Python在競賽中的應用場景環境搭建對競賽效率的影響 競賽環境需求分析 常見競賽類型&#xff08;算法、數據分析、機器學習等&#xff09;不同競賽對Python版本及庫的要求硬件與操作系統的兼容性問題 Pyth…

在 Win10 上 WSL 安裝 Debian 12 后,Linux 如何啟動 SMTP 服務?

在 WSL 的 Debian 12 中啟動 SMTP 服務&#xff08;以 Postfix 為例&#xff09;&#xff0c;請按以下步驟操作&#xff1a; 1. 安裝 Postfix sudo apt update sudo apt install postfix mailutils安裝過程中會彈出配置窗口&#xff1a; General type of mail configuration&a…

樹莓派超全系列教程文檔--(59)樹莓派攝像頭rpicam-apps

這里寫目錄標題 rpicam-apps libcamera 文章來源&#xff1a; http://raspberry.dns8844.cn/documentation 原文網址 rpicam-apps 樹莓派操作系統 Bookworm 將相機捕捉應用程序從 libcamera-\* 重命名為 rpicam-*。符號鏈接允許用戶暫時使用舊名稱。盡快采用新的應用程序名稱…

【數據結構】圖論最短路徑算法深度解析:從BFS基礎到全算法綜述?

最短路徑 導讀一、最短路徑1.1 單源最短路徑1.2 各頂點間的最短路徑1.3 最短路徑算法 二、BFS算法結語內容回顧下一篇預告&#xff1a;挑戰帶權最短路徑&#xff01; 導讀 大家好&#xff0c;很高興又和大家見面啦&#xff01;&#xff01;&#xff01; 歡迎繼續探索圖算法的…

中國政務數據安全建設細化及市場需求分析

(基于新《政務數據共享條例》及相關法規) 一、引言 近年來,中國政府高度重視數字政府建設和數據要素市場化配置改革。《政務數據共享條例》(以下簡稱“《共享條例》”)的發布,與《中華人民共和國數據安全法》(以下簡稱“《數據安全法》”)、《中華人民共和國個人信息…

Linux信號保存與處理機制詳解

Linux信號的保存與處理涉及多個關鍵機制&#xff0c;以下是詳細的總結&#xff1a; 1. 信號的保存 進程描述符&#xff08;task_struct&#xff09;&#xff1a;每個進程的PCB中包含信號相關信息。 pending信號集&#xff1a;記錄已到達但未處理的信號&#xff08;未決信號&a…

【Redis】筆記|第10節|京東HotKey實現多級緩存架構

緩存架構 京東HotKey架構 代碼結構 代碼詳情 功能點&#xff1a;&#xff08;如代碼有錯誤&#xff0c;歡迎討論糾正&#xff09; 多級緩存&#xff0c;先查HotKey緩存&#xff0c;再查Redis&#xff0c;最后才查數據庫熱點數據重建邏輯使用分布式鎖&#xff0c;二次查詢更新…

php apache構建 Web 服務器

虛擬機配置流程winsever2016配置Apache、Mysql、php_windows server 2016配置web服務器-CSDN博客 PHP 和 Apache 通過 ??模塊化協作?? 共同構建 Web 服務器&#xff0c;以下是它們的交互機制和工作流程&#xff1a; ??一、核心組件分工?? 組件角色??Apache??Web …

二分查找排序講解

一、二分查找&#xff08;Binary Search&#xff09; 核心思想&#xff1a; 前提&#xff1a;數組必須是 有序的&#xff08;比如從小到大排列&#xff09;。目標&#xff1a;在數組中快速找到某個數&#xff08;比如找 7&#xff09;。方法&#xff1a;每次排除一半的數&…