news 2026/9/10 14:32:05

Solidity 智能合约安全实战指南:基于 agents 市场 solidity-security 技能的重入、溢出、访问控制与审计模式全解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Solidity 智能合约安全实战指南:基于 agents 市场 solidity-security 技能的重入、溢出、访问控制与审计模式全解

Solidity 智能合约安全实战指南:基于 agents 市场 solidity-security 技能的重入、溢出、访问控制与审计模式全解

【免费下载链接】agentsMulti-harness agentic plugin marketplace for Claude Code, Codex, Cursor, OpenCode, GitHub Copilot, and Google Antigravity项目地址: https://gitcode.com/GitHub_Trending/agents24/agents

本文以当前开源仓库中blockchain-web3插件的solidity-security技能为蓝本,系统讲解智能合约最常见的高危漏洞及其安全防御模式,覆盖 Checks-Effects-Interactions(CEI)、Pull Over Push、Emergency Stop、Gas 优化与 Hardhat 安全回归测试等完整实战方案。读完本文,你将掌握一套可直接照抄、可运行、可复用的"脆弱代码 → 安全代码"对照模板,并能独立完成合约安全审计前的自检与面向专业审计的代码整理。

本文核心素材来自 SKILL.md 及其"渐进式披露"的深层文档 references/details.md,两者共同构成了该技能的两层内容:外层导航层提供"何时使用 + 安全测试 + 审计准备",内层 details 层提供四大高危漏洞的脆弱/安全对照代码、安全最佳实践与 Gas 优化模式。

一、技能文档概览:solidity-security 能做什么

该技能位于仓库blockchain-web3插件下,其 SKILL.md 的 frontmatter 这样定义自己的用途:

Master smart contract security best practices to prevent common vulnerabilities and implement secure Solidity patterns. Use when writing smart contracts, auditing existing contracts, or implementing security measures for blockchain applications.

即:在编写智能合约、审计既有合约、为区块链应用实施安全防护时启用。其能力覆盖:

  • 编写安全的智能合约;
  • 审计既有合约中的漏洞;
  • 实现安全的 DeFi 协议;
  • 预防重入(reentrancy)、溢出(overflow)、访问控制(access control)问题;
  • 在保持安全的前提下优化 Gas;
  • 为专业审计做准备;
  • 理解常见攻击向量。

这与仓库中同插件的 blockchain-developer.md Agent 的定位相互印证——该 Agent 明确将"智能合约安全审计:重入、溢出、访问控制漏洞""Gas 优化技术与合约体积最小化""使用 Certora、Slither、Mythril 等工具的正式验证"列为能力项。两者配合使用:Agent 负责整体开发决策,solidity-security 技能提供具体漏洞模式与代码模板。

二、在 agents 仓库中的定位与使用方式

这是一个Multi-harness agentic plugin marketplace(多运行环境智能体插件市场),按 docs/plugins.md 的说明,整个市场包含 94 个插件,每个插件按agents/commands/skills/三类组件组织。blockchain-web3插件属于"🔗 Blockchain"分类,安装命令为:

/plugin install blockchain-web3

安装后只会把该插件自己的 Agent、命令与技能加载进上下文。solidity-security是插件下四个技能之一,目录结构为:

plugins/blockchain-web3/ ├── agents/ │ └── blockchain-developer.md # 该领域的主 Agent └── skills/ ├── defi-protocol-templates/ # DeFi 协议模板(Staking、AMM 等) ├── nft-standards/ # NFT 标准 ├── solidity-security/ # ← 本文主角 │ ├── SKILL.md # 导航层:何时用、安全测试、审计准备 │ └── references/details.md # 细节层:漏洞对照代码与全部模式 └── web3-testing/ # Web3 合约测试(Hardhat/Foundry)

技能采用"渐进式披露"结构:Agent 或 LLM 先在导航层 SKILL.md 判断任务匹配度,当导航层信息不足时,再读取深层的 references/details.md。因此本文后续将按同样的两层顺序展开,确保不漏掉任何一处可执行的细节。

三、四大高危漏洞与安全模式对照

references/details.md 将最关键的知识浓缩为"脆弱代码 → 安全代码"的直接对照。下面逐一拆解。

3.1 重入攻击(Reentrancy)

原理:攻击者在合约状态更新之前,通过外部调用回调进入你的合约,从而重复提取资金。它的根因是"先做外部交互、后改状态"的错误顺序。

脆弱代码balances[msg.sender] = 0发生在外部调用之后,形同虚设):

// VULNERABLE TO REENTRANCY contract VulnerableBank { mapping(address => uint256) public balances; function withdraw() public { uint256 amount = balances[msg.sender]; // DANGER: External call before state update (bool success, ) = msg.sender.call{value: amount}(""); require(success); balances[msg.sender] = 0; // Too late! } }

攻击者合约的 fallback 会在call触发时再次进入withdraw,而此时余额尚未清零,可以无限次重复取款——这正是历史上著名智能合约攻击(如 2016 年 The DAO 事件)采用的核心手法,也是每一轮 DeFi 审计中最优先检查的项。

安全模式一:Checks-Effects-Interactions(先检查、再改状态、最后外部交互)

contract SecureBank { mapping(address => uint256) public balances; function withdraw() public { uint256 amount = balances[msg.sender]; require(amount > 0, "Insufficient balance"); // EFFECTS: Update state BEFORE external call balances[msg.sender] = 0; // INTERACTIONS: External call last (bool success, ) = msg.sender.call{value: amount}(""); require(success, "Transfer failed"); } }

关键差异只有一行顺序:先在 EFFECTS 阶段把余额清零,攻击者重入时读到的余额已经是 0,require(amount > 0)会直接拒绝第二轮调用。

安全模式二:ReentrancyGuard 互斥锁(多函数间交叉重入时的兜底)

import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; contract SecureBank is ReentrancyGuard { mapping(address => uint256) public balances; function withdraw() public nonReentrant { uint256 amount = balances[msg.sender]; require(amount > 0, "Insufficient balance"); balances[msg.sender] = 0; (bool success, ) = msg.sender.call{value: amount}(""); require(success, "Transfer failed"); } }

nonReentrant修饰符相当于一把一次性互斥锁,同一事务内禁止嵌套调用被它保护的函数。实践建议:CEI 是纪律、Guard 是保险——只要对外部调用不够确定,就把两者叠加。注意 OpenZeppelin 的导入路径会随版本调整(v4 位于security/,更新版本中可能移动),请以你工程内实际安装的版本为准。

3.2 整数溢出 / 下溢(Integer Overflow / Underflow)

原理:无符号整数减法可以"回绕"成一个巨大数字,加法可以溢出为 0,从而绕过余额检查或凭空增发代币。

脆弱代码(Solidity < 0.8.0)

// VULNERABLE contract VulnerableToken { mapping(address => uint256) public balances; function transfer(address to, uint256 amount) public { // No overflow check - can wrap around balances[msg.sender] -= amount; // Can underflow! balances[to] += amount; // Can overflow! } }

安全模式一(Solidity >= 0.8.0):编译器内置了算术检查,溢出或下溢会自动 revert,无需额外代码:

// Solidity 0.8+ has built-in overflow/underflow checks contract SecureToken { mapping(address => uint256) public balances; function transfer(address to, uint256 amount) public { // Automatically reverts on overflow/underflow balances[msg.sender] -= amount; balances[to] += amount; } }

安全模式二(Solidity < 0.8.0):使用 OpenZeppelin SafeMath 显式检查:

import "@openzeppelin/contracts/utils/math/SafeMath.sol"; contract SecureToken { using SafeMath for uint256; mapping(address => uint256) public balances; function transfer(address to, uint256 amount) public { balances[msg.sender] = balances[msg.sender].sub(amount); balances[to] = balances[to].add(amount); } }

这里的技术要点是版本分水岭 0.8.0:低于此版本必须显式启用 SafeMath(或unchecked之外的检查),0.8.0 及以上则依赖内置检查。审计存量合约时,第一件事就是确认pragma solidity版本及其是否全面启用了安全算术。

3.3 访问控制缺失(Access Control)

原理:关键函数(提款、铸造、升级、暂停)没有权限校验,任何地址都可以调用。

脆弱代码withdraw无任何限制,任何人都可以把合约余额取走:

// VULNERABLE: Anyone can call critical functions contract VulnerableContract { address public owner; function withdraw(uint256 amount) public { // No access control! payable(msg.sender).transfer(amount); } }

安全模式一:继承 OpenZeppelin Ownable

import "@openzeppelin/contracts/access/Ownable.sol"; contract SecureContract is Ownable { function withdraw(uint256 amount) public onlyOwner { payable(owner()).transfer(amount); } }

安全模式二:自定义角色映射 + 修饰符(适用于多管理员、分层权限场景):

// Or implement custom role-based access contract RoleBasedContract { mapping(address => bool) public admins; modifier onlyAdmin() { require(admins[msg.sender], "Not an admin"); _; } function criticalFunction() public onlyAdmin { // Protected function } }

审计要点:在 references/details.md 给出的"常见漏洞自查清单"里,访问控制与"不把tx.origin用于认证(应使用msg.sender)"是并列的两条独立检查项——前者控制"谁能调用",后者防止钓鱼合约借tx.origin冒充用户。

3.4 抢跑攻击(Front-Running)

原理:交易提交后先进入公开的内存池(mempool)等待打包,矿工或机器人可以观察到你的交易参数,抢先提交一笔相同参数的交易,赚取差价(经典场景是 DEX 大额兑换被抢先吃走滑点)。

脆弱代码swapminOutput与金额全部公开可见:

// VULNERABLE TO FRONT-RUNNING contract VulnerableDEX { function swap(uint256 amount, uint256 minOutput) public { // Attacker sees this in mempool and front-runs uint256 output = calculateOutput(amount); require(output >= minOutput, "Slippage too high"); // Perform swap } }

缓解手段:Commit-Reveal(先提交承诺、后揭示)

contract SecureDEX { mapping(bytes32 => bool) public usedCommitments; // Step 1: Commit to trade function commitTrade(bytes32 commitment) public { usedCommitments[commitment] = true; } // Step 2: Reveal trade (next block) function revealTrade( uint256 amount, uint256 minOutput, bytes32 secret ) public { bytes32 commitment = keccak256(abi.encodePacked( msg.sender, amount, minOutput, secret )); require(usedCommitments[commitment], "Invalid commitment"); // Perform swap } }

承诺阶段只上链一个keccak256哈希,攻击者无法在揭示前知道真实参数;揭示阶段用msg.sender + amount + minOutput + secret重新计算哈希并与承诺比对,从而锁定"这个交易只能由承诺者本人按原参数执行"。usedCommitments映射同时保证每个承诺只能被消费一次。

四、安全最佳实践模式库

在上述四大漏洞之上,references/details.md 还沉淀了一组更高层、可组合的设计模式。

4.1 Checks-Effects-Interactions(CEI)完整范式

任何涉及外部调用的函数都应遵循三段式,且三段顺序不可调换:

contract SecurePattern { mapping(address => uint256) public balances; function withdraw(uint256 amount) public { // 1. CHECKS: Validate conditions require(amount <= balances[msg.sender], "Insufficient balance"); require(amount > 0, "Amount must be positive"); // 2. EFFECTS: Update state balances[msg.sender] -= amount; // 3. INTERACTIONS: External calls last (bool success, ) = msg.sender.call{value: amount}(""); require(success, "Transfer failed"); } }

CHECKS 负责把所有require前置;EFFECTS 在外部交互前完成全部状态写入;INTERACTIONS 放在最后,并对call的返回值做require(success)检查——"检查外部调用返回值"也是漏洞自查清单中的独立条目。这样即便外部调用被恶意重入,状态早已不可再次利用。

4.2 Pull Over Push(拉取优于推送)

资金分发场景中,让每个收款人自己来取(pull)远优于合约逐个向外转账(push)

// Prefer this (pull) contract SecurePayment { mapping(address => uint256) public pendingWithdrawals; function recordPayment(address recipient, uint256 amount) internal { pendingWithdrawals[recipient] += amount; } function withdraw() public { uint256 amount = pendingWithdrawals[msg.sender]; require(amount > 0, "Nothing to withdraw"); pendingWithdrawals[msg.sender] = 0; payable(msg.sender).transfer(amount); } } // Over this (push) contract RiskyPayment { function distributePayments(address[] memory recipients, uint256[] memory amounts) public { for (uint i = 0; i < recipients.length; i++) { // If any transfer fails, entire batch fails payable(recipients[i]).transfer(amounts[i]); } } }

Push 模式的致命缺陷写在注释里:只要recipients数组中有一个地址是拒绝接收 ETH 的合约(或 gas 不足),整个循环 revert,全部用户资金被"卡死"。Pull 模式把失败风险转移给单个收款人,同时天然契合 CEI(先记待取账、再清零、后转账)。凡是"批量发钱"的逻辑,都应当改写成记账式拉取。

4.3 输入验证(Input Validation)

把边界检查全部前置到函数入口,用require拒绝非法输入:

contract SecureContract { function transfer(address to, uint256 amount) public { // Validate inputs require(to != address(0), "Invalid recipient"); require(to != address(this), "Cannot send to contract"); require(amount > 0, "Amount must be positive"); require(amount <= balances[msg.sender], "Insufficient balance"); // Proceed with transfer balances[msg.sender] -= amount; balances[to] += amount; } }

四个require分别拦掉了:零地址转账、把资金转给自己合约(常见于误操作/陷阱)、零金额交易、超余额交易。同类插件 web3-testing/SKILL.md 中的 Foundry 测试也专门用vm.expectRevert("Invalid recipient")验证"转账到零地址必须失败",与这里的错误信息完全对齐,说明输入验证是与测试联动设计的。

4.4 应急熔断(Emergency Stop / Circuit Breaker)

发现漏洞时,第一时间冻结关键功能,为修复争取时间:

import "@openzeppelin/contracts/security/Pausable.sol"; contract EmergencyStop is Pausable, Ownable { function criticalFunction() public whenNotPaused { // Function logic } function emergencyStop() public onlyOwner { _pause(); } function resume() public onlyOwner { _unpause(); } }

继承Pausable后,所有加了whenNotPaused的函数在熔断期间一律 revert;emergencyStop/resume只对owner开放。把熔断权限与多签/时间锁(timelock)结合,是专业 DeFi 协议的标准配置。这一模式也呼应了 blockchain-developer.md 中"优先使用久经考验的库与既有模式"的行为准则。

五、安全前提下的 Gas 优化

安全与效率不是对立的——details 文档明确将 Gas 优化列入安全技能(references/details.md),因为更高 Gas 消耗本身就是 DoS 攻击面。文档给出四条可直接套用的规则。

5.1 用uint256而非更小类型

EVM 的存储槽固定 256 位宽,uint8并不会省存储,反而可能引入额外的类型转换开销:

// More gas efficient contract GasEfficient { uint256 public value; // Optimal function set(uint256 _value) public { value = _value; } } // Less efficient contract GasInefficient { uint8 public value; // Still uses 256-bit slot function set(uint8 _value) public { value = _value; // Extra gas for type conversion } }

单值场景直接用uint256;更小类型只在需要把多个变量塞进同一存储槽(见下一条)时才有意义。

5.2 打包存储变量(Storage Packing)

把较小的类型相邻声明,让多个变量共享一个 256 位存储槽,写槽次数从 N 次降为 1 次:

// Gas efficient (3 variables in 1 slot) contract PackedStorage { uint128 public a; // Slot 0 uint64 public b; // Slot 0 uint64 public c; // Slot 0 uint256 public d; // Slot 1 } // Gas inefficient (each variable in separate slot) contract UnpackedStorage { uint256 public a; // Slot 0 uint256 public b; // Slot 1 uint256 public c; // Slot 2 uint256 public d; // Slot 3 }

uint128 + uint64 + uint64 = 256,恰好填满一个槽;而四个uint256各占一个槽共 4 次 SSTORE。打包后仅需 2 次 SSTORE。这是所有存储密集型合约(代币、账本、治理)的首选优化。

5.3 函数参数用calldata而非memory

只读的外部入参声明为calldata可直接引用交易数据区,避免先拷贝到内存:

contract GasOptimized { // More gas efficient function processData(uint256[] calldata data) public pure returns (uint256) { return data[0]; } // Less efficient function processDataMemory(uint256[] memory data) public pure returns (uint256) { return data[0]; } }

5.4 适当用 Event 代替存储

发事件的开销远低于写合约存储。对"仅需可追溯、无需链上读取"的数据,用事件落账即可:

contract EventStorage { // Emitting events is cheaper than storage event DataStored(address indexed user, uint256 indexed id, bytes data); function storeData(uint256 id, bytes calldata data) public { emit DataStored(msg.sender, id, data); // Don't store in contract storage unless needed } }

注意两个indexed参数:索引字段可被链下索引器(如 The Graph)高效检索。在web3-testing技能的 Hardhat 示例中也有对应的"应正确发出 Transfer 事件"的断言模式(expect(...).to.emit(token, "Transfer")),说明事件既是存储优化手段,也是链下集成的公共接口,规范发出事件同时是审计清单条目之一。

六、用 Hardhat 把"攻击"写进测试:安全回归测试实战

SKILL.md 给出的核心可执行产出,是一组把前三类漏洞当作回归测试用例的 Hardhat 测试。其思路是:真正部署攻击者合约去"打"你的安全合约,并断言攻击被 revert——这是证明防护有效的最直接证据。

// Hardhat test example const { expect } = require("chai"); const { ethers } = require("hardhat"); describe("Security Tests", function () { it("Should prevent reentrancy attack", async function () { const [attacker] = await ethers.getSigners(); const VictimBank = await ethers.getContractFactory("SecureBank"); const bank = await VictimBank.deploy(); const Attacker = await ethers.getContractFactory("ReentrancyAttacker"); const attackerContract = await Attacker.deploy(bank.address); // Deposit funds await bank.deposit({ value: ethers.utils.parseEther("10") }); // Attempt reentrancy attack await expect( attackerContract.attack({ value: ethers.utils.parseEther("1") }), ).to.be.revertedWith("ReentrancyGuard: reentrant call"); }); it("Should prevent integer overflow", async function () { const Token = await ethers.getContractFactory("SecureToken"); const token = await Token.deploy(); // Attempt overflow await expect(token.transfer(attacker.address, ethers.constants.MaxUint256)) .to.be.reverted; }); it("Should enforce access control", async function () { const [owner, attacker] = await ethers.getSigners(); const Contract = await ethers.getContractFactory("SecureContract"); const contract = await Contract.deploy(); // Attempt unauthorized withdrawal await expect(contract.connect(attacker).withdraw(100)).to.be.revertedWith( "Ownable: caller is not the owner", ); }); });

三个用例的价值在于断言信息与前面安全代码中的 revert 文案严格对应

  • 重入用例断言"ReentrancyGuard: reentrant call",对应 3.1 节SecureBank is ReentrancyGuardnonReentrant的默认报错,同时反向证明了攻击者合约确实尝试了重入;
  • 溢出用例用ethers.constants.MaxUint256直接向目标转账,断言 Solidity 0.8+ 内置检查会 revert(只断言 revert、不关心具体文案,因为内置检查文案在不同编译器版本间不统一);
  • 访问控制用例用contract.connect(attacker)以非 owner 身份调用withdraw,断言"Ownable: caller is not the owner",与 3.3 节SecureContract is Ownable+onlyOwner一一呼应。

这种"红队测试驱动"的写法,把漏洞预防从"代码审查靠肉眼"升级为"每次 CI 都真实重放一次攻击"。若需要更全面的测试基建(Foundry 模糊测试、主网 fork、账户模拟、Gas 报告),可直接复用同插件 web3-testing/SKILL.md 中现成的hardhat.config.js(含solidity-coveragehardhat-gas-reporter、主网 fork 配置)与 Forge 测试模板。

七、面向专业审计的合约编写

正式提交第三方审计前,代码应达到"可审计"的文档标准。SKILL.md 提供了一个最佳实践范例WellDocumentedContract,它同时示范了三件事:合约级 NatSpec函数级 NatSpec、以及用注释明确标注 CEI 各阶段

contract WellDocumentedContract { /** * @title Well Documented Contract * @dev Example of proper documentation for audits * @notice This contract handles user deposits and withdrawals */ /// @notice Mapping of user balances mapping(address => uint256) public balances; /** * @dev Deposits ETH into the contract * @notice Anyone can deposit funds */ function deposit() public payable { require(msg.value > 0, "Must send ETH"); balances[msg.sender] += msg.value; } /** * @dev Withdraws user's balance * @notice Follows CEI pattern to prevent reentrancy * @param amount Amount to withdraw in wei */ function withdraw(uint256 amount) public { // CHECKS require(amount <= balances[msg.sender], "Insufficient balance"); // EFFECTS balances[msg.sender] -= amount; // INTERACTIONS (bool success, ) = msg.sender.call{value: amount}(""); require(success, "Transfer failed"); } }

可审计代码的三个要领:

  1. 声明意图:合约级用@title/@notice一句话说清"这个合约管什么资金、做什么事",函数级说明调用条件与副作用;
  2. 声明安全策略:如@notice Follows CEI pattern to prevent reentrancy——直接告诉审计员"我在这里应用了哪个模式、防的是什么",大幅降低漏审风险;
  3. 代码内留安全标注:把CHECKS/EFFECTS/INTERACTIONS写成注释分区,让审计流程可以逐区核对,而不是在整段逻辑里找状态变更点。

这也与 blockchain-developer.md 的 Response Approach 第 6 条"记录合约行为并产出审计就绪的代码文档"的要求吻合。

八、上线前漏洞自查清单

references/details.md 以一份"清单合约"的形式收束全部要点。虽然它不能编译执行,但其注释本身就是上线前的逐项审查表

// Security Checklist Contract contract SecurityChecklist { /** * [ ] Reentrancy protection (ReentrancyGuard or CEI pattern) * [ ] Integer overflow/underflow (Solidity 0.8+ or SafeMath) * [ ] Access control (Ownable, roles, modifiers) * [ ] Input validation (require statements) * [ ] Front-running mitigation (commit-reveal if applicable) * [ ] Gas optimization (packed storage, calldata) * [ ] Emergency stop mechanism (Pausable) * [ ] Pull over push pattern for payments * [ ] No delegatecall to untrusted contracts * [ ] No tx.origin for authentication (use msg.sender) * [ ] Proper event emission * [ ] External calls at end of function * [ ] Check return values of external calls * [ ] No hardcoded addresses * [ ] Upgrade mechanism (if proxy pattern) */ }

15 项清单可归并为四条主线,方便记忆与执行:

  • 状态一致性:重入防护(Guard 或 CEI)、外部调用置于函数末尾并检查返回值——对应 3.1 与 4.1 节;
  • 数值与身份:0.8+ 或 SafeMath 防溢出;只用msg.sender认证、绝不信任tx.origin;函数入口全面require输入校验——对应 3.2、3.3、4.3 节;
  • 抗操控与韧性:对公开可抢跑的交易使用 commit-reveal;资金用 Pull Over Push;预留 Pausable 熔断;不向不可信合约delegatecall——对应 3.4、4.2、4.4 节;
  • 工程与审计:存储打包 +calldata控制成本;事件规范发出;地址不硬编码而走构造函数参数/配置;若采用可升级代理则设计并审查升级机制。

九、总结

solidity-security 技能把智能合约安全从"抽象原则"压缩成了一套可直接对照修改的代码模板库:四大高危漏洞(重入、溢出、访问控制、抢跑)各自给出脆弱版与安全版;安全最佳实践(CEI、Pull Over Push、输入验证、熔断)提供可组合的设计范式;Gas 优化四则保证安全与效率兼得;Hardhat 攻击回归测试让防护可被持续验证;审计准备范例与 15 项自查清单让合约达到专业审计门槛。全部代码均可从 SKILL.md 与 references/details.md 直接复制使用,并与 blockchain-developer.md Agent、web3-testing/SKILL.md 测试技能形成"写 → 测 → 审"完整闭环。对于正在开发 DeFi、NFT 或任何托管用户资产的团队,把本文的"脆弱代码"当作黑名单、把"安全模式"当作默认写法,即是抵御绝大多数已知攻击向量最经济有效的起点。

【免费下载链接】agentsMulti-harness agentic plugin marketplace for Claude Code, Codex, Cursor, OpenCode, GitHub Copilot, and Google Antigravity项目地址: https://gitcode.com/GitHub_Trending/agents24/agents

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/10 14:30:00

YOLOv5+ArcFace人脸检测与特征提取工程闭环实践

简介&#xff1a;本资源是一套基于YOLOv5与ArcFace的人脸检测与识别完整实现方案&#xff0c;面向计算机视觉初学者及AI项目开发者&#xff0c;解决从人脸定位到特征匹配的一体化技术落地问题&#xff0c;适用于安防监控、门禁系统、身份核验等实际场景。压缩包共54个文件&…

作者头像 李华
网站建设 2026/9/10 14:25:50

Spring Boot拦截器中获取requestBody的最佳实践

1. 为什么需要获取requestBody&#xff1f; 在Spring Boot开发中&#xff0c;拦截器(Interceptor)是处理HTTP请求的重要组件。但很多开发者都遇到过这样的困境&#xff1a;在拦截器的preHandle方法中&#xff0c;无法直接获取到请求体(requestBody)的内容。这主要是因为Servlet…

作者头像 李华