在日常项目开发中,依赖第三方库几乎是不可避免的。从 spring-boot-starter
到 MyBatis、Log4j,再到各种工具类库,几乎每个应用都需要几十甚至上百个依赖。
然而,这些依赖并非完全安全。很多开发者可能在不知情的情况下,使用了存在 已知漏洞 的版本,轻则引发应用崩溃,重则导致远程代码执行或数据泄漏。
本文带你实现一个依赖库漏洞在线扫描仪,包含以下内容:
1、执行后自动扫描所有依赖 jar 包
2、匹配漏洞数据库,检测已知漏洞
3、将扫描结果 可视化展示,直观告诉你哪个依赖存在漏洞、漏洞危害描述、对应的 CVE 编号等信息


1. 需求痛点
1. 依赖数量庞大:一个典型的 Spring Boot 应用常常包含 100+ 依赖,人工排查几乎不可能。
2. 漏洞信息分散:CVE 公告、NVD、GitHub issue,开发者很难第一时间获取。
3. 缺乏修复指导:即使知道某个版本有漏洞,很多团队也不清楚应该升级到哪个安全版本。
因此,一个 一键扫描 + 漏洞说明 + 修复建议 的工具,能够极大提升团队安全响应效率。
2. 现状分析
目前常见方案:
OWASP Dependency-Check:功能强大,但集成较重,国内文档少。
Snyk / WhiteSource:商业化产品,部分功能需付费。
手工排查:低效且容易遗漏。
所以,我们希望在 SpringBoot 应用内部,做一个 轻量级依赖漏洞扫描仪,即插即用,足够简单。
3. 解决思路
1. 扫描依赖 :读取当前应用的所有 jar 包版本。 2. 匹配漏洞库 :根据 groupId/artifactId/version 判断是否命中漏洞。 3. 输出漏洞详情:包括 - CVE 编号 - 漏洞描述 - 危害等级(CVSS) - 修复方案(安全版本范围) - 官方参考链接
4. 前端可视化:表格展示结果,按严重性排序,高危一目了然。
4. 系统设计
依赖收集器:获取所有依赖及版本信息
漏洞在线扫描:调用相关漏洞查询库完成漏洞分析
漏洞匹配器:判断依赖版本是否落在漏洞范围
结果展示层:Spring Boot REST API + 前端表格
5. 关键实现
5.1 漏洞数据结构
定义一个漏洞实体,包含详细说明与修复方案:
typescript
public class Vulnerability {
private String cve;
private String description;
private String severity;
private String vulnerableVersions;
private String safeVersion;
private String reference;
public boolean isVersionVulnerable(String version) {
// 可用 Maven ArtifactVersion 进行范围匹配
return VersionRangeChecker.isVulnerable(version, vulnerableVersions);
}
}
示例漏洞数据(JSON 文件):
css
[ { "groupId": "org.apache.logging.log4j", "artifactId": "log4j-core", "vulnerableVersions": "<=2.14.1", "safeVersion": "2.15.0+", "cve": "CVE-2021-44228", "description": "Log4j 2 JNDI 远程代码执行漏洞,攻击者可通过恶意日志消息执行任意代码。", "severity": "Critical", "reference": "https://nvd.nist.gov/vuln/detail/CVE-2021-44228" }]
5.2 扫描依赖并匹配漏洞
typescript
@GetMapping("/dependencies/scan")
public ResponseEntity<Map<String, Object>> scanDependencies() {
logger.info("开始执行依赖扫描...");
long startTime = System.currentTimeMillis();
try {
// 1. 收集依赖
List<DependencyInfo> dependencies = dependencyCollector.collect();
logger.info("收集到 {} 个依赖", dependencies.size());
// 2. 匹配漏洞
List<DependencyRisk> risks = vulnerabilityMatcher.matchVulnerabilities(dependencies);
// 3. 按风险等级排序
risks = vulnerabilityMatcher.sortByRiskLevel(risks);
// 4. 获取统计信息
VulnerabilityMatcher.RiskStatistics statistics = vulnerabilityMatcher.getRiskStatistics(risks);
long endTime = System.currentTimeMillis();
long duration = endTime - startTime;
logger.info("依赖扫描完成,耗时 {} ms,{}", duration, statistics.toString());
// 构建响应
Map<String, Object> response = new HashMap<>();
response.put("success", true);
response.put("message", "扫描完成");
response.put("data", risks);
// 修复统计信息:总依赖数应该是所有依赖,不是只有有漏洞的
Map<String, Object> statisticsMap = new HashMap<>();
statisticsMap.put("totalDependencies", dependencies.size()); // 所有依赖数量
statisticsMap.put("vulnerableDependencies", risks.size()); // 有漏洞的依赖数量
statisticsMap.put("criticalCount", statistics.criticalCount);
statisticsMap.put("highCount", statistics.highCount);
statisticsMap.put("mediumCount", statistics.mediumCount);
statisticsMap.put("lowCount", statistics.lowCount);
statisticsMap.put("scanDuration", duration);
response.put("statistics", statisticsMap);
return ResponseEntity.ok(response);
} catch (Exception e) {
logger.error("依赖扫描失败", e);
Map<String, Object> errorResponse = new HashMap<>();
errorResponse.put("success", false);
errorResponse.put("message", "扫描失败: " + e.getMessage());
errorResponse.put("data", List.of());
return ResponseEntity.status(500).body(errorResponse);
}
}
返回的 JSON 示例:
css
[ { "groupId": "org.apache.logging.log4j", "artifactId": "log4j-core", "version": "2.14.1", "riskLevel": "Critical", "cve": "CVE-2021-44228", "description": "Log4j JNDI 注入可导致远程代码执行。", "safeVersion": "2.15.0+", "reference": "https://nvd.nist.gov/vuln/detail/CVE-2021-44228" }]
6. 前端可视化
xml
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>依赖包安全扫描仪</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
</head>
<body class="bg-gray-50 min-h-screen">
<!-- 导航栏 -->
<nav class="bg-white shadow-lg">
<div class="max-w-7xl mx-auto px-4">
<div class="flex justify-between items-center py-4">
<div class="flex items-center space-x-3">
<i class="fas fa-shield-alt text-blue-600 text-2xl"></i>
<h1 class="text-xl font-bold text-gray-800">依赖包安全扫描仪</h1>
</div>
<div class="flex items-center space-x-4">
<span class="text-sm text-gray-600">Spring Boot 2.7.18</span>
<div class="flex items-center space-x-2">
<div id="statusIndicator" class="w-2 h-2 bg-green-500 rounded-full"></div>
<div id="statusSpinner" class="loading-spinner" style="display: none;"></div>
<span id="statusText" class="text-sm">就绪</span>
</div>
</div>
</div>
</div>
</nav>
<div class="max-w-7xl mx-auto px-4 py-8">
<!-- 操作面板 -->
<div class="bg-white rounded-lg shadow-md p-6 mb-8">
<div class="flex flex-col space-y-4">
<div class="flex flex-col sm:flex-row justify-between items-start sm:items-center space-y-4 sm:space-y-0">
<div>
<h2 class="text-lg font-semibold text-gray-800 mb-2">扫描控制</h2>
<p class="text-gray-600 text-sm">扫描当前应用的所有依赖包,检测已知安全漏洞</p>
</div>
<div class="flex space-x-3">
<button
id="scanButton"
class="bg-blue-600 hover:bg-blue-700 disabled:bg-gray-400 text-white px-6 py-2 rounded-lg font-medium transition-colors duration-200 flex items-center space-x-2">
<i class="fas fa-search mr-2"></i>开始扫描
</button>
<button
id="exportButton"
class="bg-green-600 hover:bg-green-700 disabled:bg-gray-400 text-white px-6 py-2 rounded-lg font-medium transition-colors duration-200 flex items-center space-x-2">
<i class="fas fa-download mr-2"></i>导出结果
</button>
</div>
</div>
<!-- 风险等级过滤器 -->
<div id="riskFilters" class="flex flex-wrap items-center space-x-4 pt-4 border-t border-gray-200" style="display: none;">
<span class="text-sm font-medium text-gray-600">风险等级过滤:</span>
<label class="flex items-center space-x-2">
<input type="checkbox" id="filter-all" class="rounded border-gray-300 text-blue-600 focus:ring-blue-500" checked>
<span class="text-sm text-gray-700">全部</span>
</label>
<label class="flex items-center space-x-2">
<input type="checkbox" id="filter-critical" class="rounded border-gray-300 text-red-600 focus:ring-red-500" checked>
<span class="text-sm text-red-700">严重</span>
</label>
<label class="flex items-center space-x-2">
<input type="checkbox" id="filter-high" class="rounded border-gray-300 text-orange-600 focus:ring-orange-500" checked>
<span class="text-sm text-orange-700">高危</span>
</label>
<label class="flex items-center space-x-2">
<input type="checkbox" id="filter-medium" class="rounded border-gray-300 text-yellow-600 focus:ring-yellow-500" checked>
<span class="text-sm text-yellow-700">中危</span>
</label>
<label class="flex items-center space-x-2">
<input type="checkbox" id="filter-low" class="rounded border-gray-300 text-blue-600 focus:ring-blue-500" checked>
<span class="text-sm text-blue-700">低危</span>
</label>
</div>
</div>
</div>
<!-- 统计信息 -->
<div id="statisticsContainer" class="statistics-grid mb-6" style="display: none;">
<div class="statistics-card bg-white rounded-lg shadow-md p-6">
<div class="flex items-center">
<div class="p-3 rounded-full bg-blue-100 text-blue-600">
<i class="fas fa-cube text-xl"></i>
</div>
<div class="ml-4">
<p class="text-sm font-medium text-gray-600">总依赖数</p>
<p id="totalDependencies" class="text-2xl font-bold text-gray-900">0</p>
</div>
</div>
</div>
<div class="statistics-card bg-white rounded-lg shadow-md p-6">
<div class="flex items-center">
<div class="p-3 rounded-full bg-red-100 text-red-600">
<i class="fas fa-exclamation-triangle text-xl"></i>
</div>
<div class="ml-4">
<p class="text-sm font-medium text-gray-600">存在漏洞</p>
<p id="vulnerableDependencies" class="text-2xl font-bold text-gray-900">0</p>
</div>
</div>
</div>
<div class="statistics-card bg-white rounded-lg shadow-md p-6">
<div class="flex items-center">
<div class="p-3 rounded-full bg-red-100 text-red-600">
<i class="fas fa-fire text-xl"></i>
</div>
<div class="ml-4">
<p class="text-sm font-medium text-gray-600">严重/高危</p>
<p id="criticalHighCount" class="text-2xl font-bold text-red-600">0</p>
</div>
</div>
</div>
<div class="statistics-card bg-white rounded-lg shadow-md p-6">
<div class="flex items-center">
<div class="p-3 rounded-full bg-yellow-100 text-yellow-600">
<i class="fas fa-exclamation-circle text-xl"></i>
</div>
<div class="ml-4">
<p class="text-sm font-medium text-gray-600">中危/低危</p>
<p id="mediumLowCount" class="text-2xl font-bold text-yellow-600">0</p>
</div>
</div>
</div>
<div class="statistics-card bg-white rounded-lg shadow-md p-6">
<div class="flex items-center">
<div class="p-3 rounded-full bg-purple-100 text-purple-600">
<i class="fas fa-clock text-xl"></i>
</div>
<div class="ml-4">
<p class="text-sm font-medium text-gray-600">扫描耗时</p>
<p id="scanDuration" class="text-2xl font-bold text-gray-900">0s</p>
</div>
</div>
</div>
</div>
<!-- 加载状态 -->
<div id="loadingSpinner" class="text-center py-8" style="display: none;">
<i class="fas fa-spinner fa-spin text-4xl text-blue-600 mb-4"></i>
<p class="text-gray-600">正在扫描依赖包...</p>
</div>
<!-- 扫描结果 -->
<div id="scanResultsContainer" class="bg-white rounded-lg shadow-md" style="display: none;">
<div class="px-6 py-4 border-b border-gray-200">
<h3 class="text-lg font-semibold text-gray-800">扫描结果</h3>
<p class="text-sm text-gray-600 mt-1">发现 <span id="resultCount">0</span> 个存在安全漏洞的依赖包</p>
</div>
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-gray-200 compact-table">
<thead class="bg-gray-50">
<tr>
<th class="text-left text-xs font-medium text-gray-500 uppercase tracking-wider dependency-cell">依赖包</th>
<th class="text-left text-xs font-medium text-gray-500 uppercase tracking-wider version-cell">版本</th>
<th class="text-left text-xs font-medium text-gray-500 uppercase tracking-wider risk-cell">风险</th>
<th class="text-left text-xs font-medium text-gray-500 uppercase tracking-wider cve-cell">CVE编号</th>
<th class="text-left text-xs font-medium text-gray-500 uppercase tracking-wider safe-version-cell">安全版本</th>
<th class="text-left text-xs font-medium text-gray-500 uppercase tracking-wider details-cell">详情</th>
<th class="text-left text-xs font-medium text-gray-500 uppercase tracking-wider action-cell">操作</th>
</tr>
</thead>
<tbody id="resultsTableBody" class="bg-white divide-y divide-gray-200">
<!-- 动态生成的扫描结果行 -->
</tbody>
</table>
</div>
</div>
<!-- 无结果提示 -->
<div id="noResultsMessage" class="bg-white rounded-lg shadow-md p-8 text-center" style="display: none;">
<i class="fas fa-check-circle text-green-500 text-4xl mb-4"></i>
<h3 class="text-lg font-semibold text-gray-800 mb-2">恭喜!未发现安全漏洞</h3>
<p class="text-gray-600">您的依赖包都是安全的,没有发现已知的安全漏洞。</p>
</div>
<!-- 错误提示 -->
<div id="errorMessage" class="bg-red-50 border border-red-200 rounded-lg p-4 mb-6" style="display: none;">
<div class="flex items-center">
<i class="fas fa-exclamation-triangle text-red-500 mr-3"></i>
<div>
<h4 class="text-red-800 font-medium">扫描失败</h4>
<p id="errorText" class="text-red-700 text-sm mt-1"></p>
</div>
</div>
</div>
</div>
<!-- 详情模态框 -->
<div id="detailModal" class="fixed inset-0 bg-gray-600 bg-opacity-50 overflow-y-auto h-full w-full z-50" style="display: none;" onclick="closeModal()">
<div class="relative top-10 mx-auto p-6 border w-11/12 md:w-4/5 lg:w-3/4 xl:w-2/3 shadow-lg rounded-lg bg-white max-h-screen overflow-y-auto" onclick="event.stopPropagation()">
<div class="flex justify-between items-center mb-6">
<div class="flex items-center space-x-3">
<div id="modalSeverityIcon" class="p-2 rounded-full">
<i class="fas fa-shield-alt text-xl"></i>
</div>
<div>
<h3 class="text-xl font-bold text-gray-800">漏洞详情与解决方案</h3>
<p class="text-sm text-gray-600" id="modalSubtitle">详细的漏洞信息和修复建议</p>
</div>
</div>
<button onclick="closeModal()" class="text-gray-400 hover:text-gray-600 transition-colors">
<i class="fas fa-times text-xl"></i>
</button>
</div>
<div id="modalContent">
<!-- 基本信息卡片 -->
<div class="bg-gray-50 rounded-lg p-4 mb-6">
<h4 class="text-lg font-semibold text-gray-800 mb-3 flex items-center">
<i class="fas fa-info-circle text-blue-600 mr-2"></i>基本信息
</h4>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">依赖包</label>
<p id="modalDependency" class="text-sm text-gray-900 font-mono bg-white p-2 rounded border"></p>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">当前版本</label>
<p id="modalVersion" class="text-sm text-gray-900 font-mono bg-white p-2 rounded border"></p>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">CVE编号</label>
<p id="modalCve" class="text-sm text-gray-900 font-mono bg-white p-2 rounded border"></p>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">风险等级</label>
<span id="modalSeverityBadge" class="inline-flex px-2 py-1 text-xs font-semibold rounded-full"></span>
</div>
</div>
</div>
<!-- 漏洞详情卡片 -->
<div class="bg-red-50 border border-red-200 rounded-lg p-4 mb-6">
<h4 class="text-lg font-semibold text-red-800 mb-3 flex items-center">
<i class="fas fa-exclamation-triangle text-red-600 mr-2"></i>漏洞详情
</h4>
<div>
<label class="block text-sm font-medium text-red-700 mb-2">漏洞描述</label>
<div id="modalDescription" class="text-sm text-red-800 bg-white p-3 rounded border leading-relaxed"></div>
</div>
<!-- 影响版本 -->
<div id="vulnerableVersionsContainer" class="mt-4" style="display: none;">
<label class="block text-sm font-medium text-red-700 mb-2">影响版本</label>
<p id="modalVulnerableVersions" class="text-sm text-red-800 bg-white p-2 rounded border font-mono"></p>
</div>
</div>
<!-- 解决方案卡片 -->
<div class="bg-green-50 border border-green-200 rounded-lg p-4 mb-6">
<h4 class="text-lg font-semibold text-green-800 mb-3 flex items-center">
<i class="fas fa-tools text-green-600 mr-2"></i>解决方案
</h4>
<!-- 推荐版本 -->
<div class="mb-4">
<label class="block text-sm font-medium text-green-800 mb-2 flex items-center">
<i class="fas fa-arrow-up text-green-600 mr-1"></i>推荐升级版本
</label>
<div class="flex items-center space-x-2 bg-white p-3 rounded border">
<p id="modalSafeVersion" class="text-sm text-green-700 font-medium flex-1"></p>
<button id="copyVersionBtn" onclick="copySafeVersion()" class="text-green-600 hover:text-green-800 p-1 rounded hover:bg-green-100 transition-colors" title="复制版本号">
<i class="fas fa-copy"></i>
</button>
</div>
<div id="upgradeHint" class="mt-2 text-xs text-green-600 flex items-center">
<i class="fas fa-lightbulb mr-1"></i>
升级到推荐版本可修复此安全漏洞
</div>
</div>
<!-- Maven 升级指令 -->
<div class="mb-4">
<label class="block text-sm font-medium text-green-800 mb-2 flex items-center">
<i class="fab fa-java text-green-600 mr-1"></i>Maven 升级指令
</label>
<div class="bg-gray-900 text-green-400 p-3 rounded font-mono text-sm relative">
<pre id="mavenCommand" class="whitespace-pre-wrap"></pre>
<button onclick="copyMavenCommand()" class="absolute top-2 right-2 text-green-400 hover:text-green-300 transition-colors" title="复制Maven指令">
<i class="fas fa-copy"></i>
</button>
</div>
</div>
<!-- Gradle 升级指令 -->
<div class="mb-4">
<label class="block text-sm font-medium text-green-800 mb-2 flex items-center">
<i class="fas fa-cube text-green-600 mr-1"></i>Gradle 升级指令
</label>
<div class="bg-gray-900 text-blue-400 p-3 rounded font-mono text-sm relative">
<pre id="gradleCommand" class="whitespace-pre-wrap"></pre>
<button onclick="copyGradleCommand()" class="absolute top-2 right-2 text-blue-400 hover:text-blue-300 transition-colors" title="复制Gradle指令">
<i class="fas fa-copy"></i>
</button>
</div>
</div>
<!-- 通用解决建议 -->
<div id="solutionTips" class="bg-white border-l-4 border-green-500 p-3 rounded">
<h5 class="font-medium text-green-800 mb-2 flex items-center">
<i class="fas fa-list-ul mr-2"></i>修复建议
</h5>
<ul id="solutionList" class="text-sm text-green-700 space-y-1 list-disc list-inside">
<!-- 动态生成解决方案列表 -->
</ul>
</div>
</div>
<!-- 相关信息 -->
<div class="bg-blue-50 border border-blue-200 rounded-lg p-4">
<h4 class="text-lg font-semibold text-blue-800 mb-3 flex items-center">
<i class="fas fa-external-link-alt text-blue-600 mr-2"></i>相关信息
</h4>
<div id="modalReferenceContainer" class="mb-3" style="display: none;">
<label class="block text-sm font-medium text-blue-700 mb-2">官方参考链接</label>
<a id="modalReference" target="_blank" class="text-sm text-blue-600 hover:text-blue-800 underline break-all"></a>
</div>
<!-- 相关资源链接 -->
<div class="grid grid-cols-1 md:grid-cols-2 gap-3">
<a id="cveDetailsLink" target="_blank" class="flex items-center p-2 text-sm text-blue-600 hover:text-blue-800 hover:bg-blue-100 rounded transition-colors">
<i class="fas fa-search mr-2"></i>CVE详情查询
</a>
<a id="mavenCentralLink" target="_blank" class="flex items-center p-2 text-sm text-blue-600 hover:text-blue-800 hover:bg-blue-100 rounded transition-colors">
<i class="fas fa-box mr-2"></i>Maven Central
</a>
<a id="osvDetailsLink" target="_blank" class="flex items-center p-2 text-sm text-blue-600 hover:text-blue-800 hover:bg-blue-100 rounded transition-colors">
<i class="fas fa-database mr-2"></i>OSV数据库
</a>
<a id="githubAdvisoryLink" target="_blank" class="flex items-center p-2 text-sm text-blue-600 hover:text-blue-800 hover:bg-blue-100 rounded transition-colors">
<i class="fab fa-github mr-2"></i>GitHub安全公告
</a>
</div>
</div>
</div>
</div>
</div>
<script src="app.js"></script>
</body>
</html>
红色标记高危漏洞
- 一行信息清楚展现漏洞 + 修复方案
- 可点击链接查看官方公告
7. 实战应用场景
1. 本地开发自查
开发者在调试时即可运行扫描,第一时间发现依赖风险。
2. 应急响应
当新漏洞爆出(如 Log4Shell),立刻扫描项目,快速定位受影响模块,并知道该升到哪个版本。
3. CI/CD 集成
在构建流水线中调用 /dependencies
API:
- 若发现高危漏洞 → 直接 fail 构建
- 提示需升级到安全版本后,才能继续部署
8. 总结
本文实现了一个轻量级的 SpringBoot 在线「依赖包漏洞扫描仪」:
自动收集依赖,无需人工干预
匹配漏洞库,输出 CVE 编号 + 漏洞描述
提供漏洞详情信息,避免知道有问题但不知如何解决
可视化展示,直观易懂