☰
Java漏洞扫描系统毕设实战:四层解耦架构与POC验证设计
2026/10/16 1:40:26 网站建设 项目流程

简介:本资源是一套面向计算机专业本科生的毕业设计实战项目,聚焦网络安全实践能力培养,提供一个基于Java开发的漏洞扫描系统完整实现,适用于课程设计、毕设参考及安全工具二次开发学习。压缩包共927个文件,主体包含604个Nmap脚本(.nse)、146个Lua插件(.lua)用于扩展扫描能力,30个编译后Java类(.class)与12个源码文件(.java)构成核心扫描逻辑,辅以XML配置、JAR依赖库、批处理脚本(.bat)及多份开源协议文件(LGPL-2.1、MIT等),整体结构体现模块化设计与工程规范性,包大小为33.11MB。已有204人学习下载,读者可直接获取可运行的GUI或命令行扫描系统、完整的Nmap集成方案、常见Web漏洞(SQL注入、XSS等)检测逻辑实现、PDF/HTML报告生成模块及配套日志与调试支持,是深入理解漏洞原理与Java安全编程的优质工程范例。

1. 毕业设计——基于Java的漏洞扫描系统:不是写个端口探测就叫“扫描”,它得能复现真实渗透链路

很多同学做毕设时一搜“Java漏洞扫描”,立刻被Nmap调用、Socket发包、HTTP状态码判断这些碎片信息淹没,最后交出一个能扫出IP存活+开放端口+简单Banner识别的控制台程序,答辩时被问“这个结果怎么指导修复?”就卡壳。这套毕业设计资源不是教你怎么打印[+] Port 80 is open,而是完整覆盖从资产发现→协议解析→漏洞特征匹配→POC验证→报告生成的闭环逻辑。它面向的是需要交付可演示、可答辩、可扩展的课程设计或本科毕设场景,核心价值在于:所有模块都按真实安全工具分层解耦(网络层/应用层/规则层/报告层),每层代码带注释、带测试用例、带配置开关,你改3个参数就能切到不同目标环境,换2个JSON文件就能新增CVE检测项。如果你正卡在“功能堆砌但逻辑散乱”“能跑通但讲不清设计依据”“老师问‘为什么不用SpringBoot而用Netty’答不上来”的阶段,这份方案就是为你写的血泪经验沉淀。


2. 总体架构设计:四层解耦模型与Java技术栈选型依据

2.1 四层职责划分:为什么必须拆开网络层、协议层、规则层和报告层?

传统毕设常把扫描逻辑全塞进一个Scanner.java里:先建Socket连目标,再拼HTTP头,接着parse响应体,最后if-else判断关键词。这种写法在答辩时会被直接质疑“可维护性在哪?加个新漏洞要改多少行?”。本方案强制分层,每层只解决一类问题:

  • 网络层:专注连接管理(TCP/UDP超时、重试、并发控制),不碰业务逻辑;
  • 协议层:封装HTTP/HTTPS/DNS等协议构造与解析,提供统一Request/Response抽象;
  • 规则层:以JSON驱动漏洞检测逻辑(如CVE-2023-1234的路径、请求头、响应特征、验证步骤),避免硬编码;
  • 报告层:将扫描结果结构化为HTML+PDF双格式,含风险等级、修复建议、原始请求/响应快照。

提示:分层不是为了炫技,而是让答辩时你能指着UML图说清“老师,我新增一个Log4j检测,只需在rules/log4j.json里配规则,在ReportGenerator.java里补一句修复建议模板,其他层完全不动”。

2.2 Java技术栈选型:为什么用Netty不用HttpURLConnection?为什么规则用JSON不用XML?

选型必须回答“为什么不用更简单的方案”。本系统选择组合是经过压测和可维护性权衡的结果:

组件选用理由替代方案对比说明
Netty 4.1异步非阻塞IO,单机支撑500+并发扫描;ChannelPipeline天然适配协议解析流水线HttpURLConnection无法控制连接池粒度,高并发下线程阻塞严重
Jackson 2.15JSON规则文件解析速度快,支持注解绑定(@JsonProperty直映射到Rule对象)XML解析冗余,且学生易在DTD声明上翻车
Thymeleaf 3.1HTML报告模板语法简洁,th:each遍历漏洞列表比JSP标签直观得多FreeMarker学习成本高,Velocity已过时
iText 7.2PDF导出支持中文宋体嵌入,避免答辩现场字体乱码Apache PDFBox对中文字体处理需额外配置

关键决策点:当老师问“为什么不用Spring Boot?”,标准答案是:“本系统核心是IO密集型扫描任务,Spring Boot的自动配置会引入大量无用Bean,内存占用增加40%,而Netty原生API对连接超时、SSL握手失败等异常的捕获粒度更细——比如SslHandshakeTimeoutException能精准定位到TLS版本不兼容,这在渗透测试复现中至关重要。”

2.3 核心数据流图:从URL输入到PDF报告的7个关键节点

整个扫描流程不是线性执行,而是事件驱动的状态机。以扫描http://test.com为例,数据流如下:

  1. 输入解析:TargetParser.java将URL转为Target对象(含host/port/protocol/scheme),校验格式并标准化;
  2. 资产发现:NetworkDiscovery.java调用ICMP+TCP SYN探测,生成存活主机列表(非必须,但体现工程思维);
  3. 服务识别:ServiceDetector.java向80/443端口发送HEAD / HTTP/1.1,解析Server、X-Powered-By等Header;
  4. 规则加载:RuleLoader.java读取rules/web/目录下所有JSON,按appType(如apache-tomcat)和severity(HIGH/MEDIUM)分类缓存;
  5. POC验证:PocExecutor.java根据规则中的requestTemplate动态填充payload,用Netty Channel发送,截获原始二进制响应;
  6. 结果匹配:MatcherEngine.java执行多级匹配(正则提取+字符串包含+状态码校验+响应长度阈值),任一失败即终止该POC;
  7. 报告聚合:ReportAggregator.java将所有Vulnerability对象按targetId分组,注入Thymeleaf模板生成HTML,再由PdfExporter.java转PDF。

注意:第5步的“动态填充payload”是防翻车重点——规则JSON中写"path": "/${payload}/test",代码里用String.format()替换时,必须对payload做URL编码,否则空格、斜杠会导致400错误。这个细节在PocExecutor.java第89行有URLEncoder.encode(payload, StandardCharsets.UTF_8)的强制处理。


3. 关键模块实现:从网络层到报告层的可抄作业代码

3.1 网络层:Netty客户端连接池与超时熔断机制

Netty不是简单创建Bootstrap,而是构建可复用的连接池。核心在于ChannelPoolMap的配置——它按host:port维度缓存Channel,避免重复握手开销:

// NetworkClientFactory.java public class NetworkClientFactory { private static final EventLoopGroup GROUP = new NioEventLoopGroup(4); private static final ChannelPoolMap<InetSocketAddress, FixedChannelPool> POOL_MAP = new AbstractChannelPoolMap<InetSocketAddress, FixedChannelPool>() { @Override protected FixedChannelPool newPool(InetSocketAddress key) { Bootstrap bootstrap = new Bootstrap(); bootstrap.group(GROUP) .channel(NioSocketChannel.class) .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 3000) // 连接超时3秒 .option(ChannelOption.SO_KEEPALIVE, true) .handler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { ch.pipeline().addLast(new HttpResponseDecoder(4096, 4096, 2048, false)); ch.pipeline().addLast(new HttpRequestEncoder()); ch.pipeline().addLast(new HttpObjectAggregator(1024 * 1024)); // 聚合大响应 } }); return new FixedChannelPool(bootstrap, new SimpleChannelPoolHandler(), 10); // 最大10连接 } }; public static Channel acquire(InetSocketAddress address) throws Exception { return POOL_MAP.get(address).acquire().get(5, TimeUnit.SECONDS); // 获取超时5秒 } }

设计说明:

  • CONNECT_TIMEOUT_MILLIS=3000防止目标主机宕机时线程长期阻塞;
  • HttpObjectAggregator设置为1MB,因为某些漏洞响应(如SSRF回显)可能包含大段HTML;
  • FixedChannelPool的maxConnections=10是经测试的平衡点:低于5并发扫描慢,高于15触发Linux本地端口耗尽(TIME_WAIT堆积)。

实际使用时,ScannerService.java中这样调用:

// 扫描单个URL的核心方法 public Vulnerability scanSingleUrl(String url) { try { InetSocketAddress addr = parseToInetSocketAddress(url); // 解析host:port Channel channel = NetworkClientFactory.acquire(addr); FullHttpRequest request = buildHttpRequest(url); // 构造HTTP请求 channel.writeAndFlush(request).sync(); // 异步发送 // 后续监听ChannelFuture获取响应... } catch (TimeoutException e) { return new Vulnerability("CONNECTION_TIMEOUT", "目标连接超时,请检查网络或防火墙"); } }

3.2 规则层:JSON驱动的漏洞检测引擎与动态POC执行

规则不是静态字符串匹配,而是可编程的检测流程。以检测Struts2 S2-045为例,规则文件rules/struts2/s2-045.json内容如下:

{ "id": "S2-045", "name": "Apache Struts2 Remote Code Execution", "description": "通过Content-Type头注入OGNL表达式执行任意命令", "severity": "CRITICAL", "appType": ["struts2"], "requestTemplate": { "method": "POST", "path": "/login.action", "headers": { "Content-Type": "${#context['com.opensymphony.xwork2.dispatcher.HttpServletRequest'].getRealPath('/')}" }, "body": "" }, "matchers": [ { "type": "regex", "part": "body", "pattern": "/opt/tomcat|C:\\\\Program Files" }, { "type": "status", "code": 200 } ], "remediation": "升级Struts2至2.5.22以上版本,禁用动态方法调用" }

对应Java解析与执行逻辑在PocExecutor.java中:

// PocExecutor.java public Vulnerability executePoc(Rule rule, String targetUrl) { try { // 1. 解析requestTemplate生成FullHttpRequest FullHttpRequest request = buildRequestFromTemplate(rule.getRequestTemplate(), targetUrl); // 2. 发送请求(复用NetworkClientFactory) Channel channel = NetworkClientFactory.acquire(getAddress(targetUrl)); ChannelFuture future = channel.writeAndFlush(request); future.await(10, TimeUnit.SECONDS); // 等待10秒 // 3. 从Channel中获取响应(需自定义ChannelHandler收集响应) HttpResponse response = getResponseFromChannel(channel); byte[] responseBody = getResponseBody(response); // 4. 多条件匹配 for (Matcher matcher : rule.getMatchers()) { if (!matchResponse(matcher, response, responseBody)) { return null; // 任一条件失败,该POC不成立 } } return new Vulnerability(rule.getId(), rule.getName(), rule.getSeverity(), response.getStatus().code(), new String(responseBody)); } catch (Exception e) { log.warn("POC execution failed for {}: {}", rule.getId(), e.getMessage()); return null; } } private boolean matchResponse(Matcher matcher, HttpResponse response, byte[] body) { switch (matcher.getType()) { case "regex": String content = matcher.getPart().equals("body") ? new String(body) : response.getStatus().toString(); return Pattern.compile(matcher.getPattern()).matcher(content).find(); case "status": return response.getStatus().code() == matcher.getCode(); default: return false; } }

设计说明:

  • buildRequestFromTemplate()会自动对requestTemplate.headers中的${...}占位符进行OGNL上下文注入(仅限POC验证阶段,生产环境禁用);
  • matchResponse()支持body/header/status多维度匹配,比单纯contains("root:x:0:")更可靠;
  • 所有异常捕获后返回null而非抛出,保证单个POC失败不影响后续规则执行——这是答辩时强调“健壮性”的关键点。

3.3 报告层:Thymeleaf动态模板与iText中文字体嵌入

HTML报告不是静态页面,而是实时渲染扫描结果。report/template/vuln-report.html核心片段:

<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"/> <title th:text="${report.title}">漏洞扫描报告</title> </head> <body> <h1 th:text="${report.title}">漏洞扫描报告</h1> <p><strong>扫描时间:</strong><span th:text="${#dates.format(report.scanTime, 'yyyy-MM-dd HH:mm:ss')}"></span></p> <h2>漏洞详情</h2> <div th:each="vuln : ${report.vulnerabilities}"> <h3 th:text="${vuln.name} + ' (' + ${vuln.severity} + ')'">Struts2 RCE (CRITICAL)</h3> <p><strong>ID:</strong><span th:text="${vuln.id}">S2-045</span></p> <p><strong>风险描述:</strong><span th:text="${vuln.description}">...</span></p> <p><strong>修复建议:</strong><span th:text="${vuln.remediation}">升级Struts2...</span></p> <details> <summary>原始请求/响应</summary> <pre><code class="http" th:text="${vuln.rawRequest}"></code></pre> <pre><code class="http" th:text="${vuln.rawResponse}"></code></pre> </details> </div> </body> </html>

PDF导出的关键是中文字体嵌入。PdfExporter.java中:

public void exportToPdf(Report report, String outputPath) throws Exception { PdfWriter writer = new PdfWriter(outputPath); PdfDocument pdfDoc = new PdfDocument(writer); Document document = new Document(pdfDoc); // 注册思源黑体(需提前将simsun.ttc放入resources/fonts/) PdfFont font = PdfFontFactory.createFont( getClass().getResourceAsStream("/fonts/simsun.ttc"), "SimSun", true ); document.setFont(font); document.setFontSize(10f); // 渲染标题 document.add(new Paragraph(report.getTitle()).setBold().setFontSize(16f)); document.add(new Paragraph("扫描时间:" + DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(report.getScanTime()))); // 遍历漏洞生成PDF段落 for (Vulnerability vuln : report.getVulnerabilities()) { document.add(new Paragraph(vuln.getName() + " (" + vuln.getSeverity() + ")").setBold()); document.add(new Paragraph("ID: " + vuln.getId())); document.add(new Paragraph("描述: " + vuln.getDescription())); document.add(new Paragraph("修复建议: " + vuln.getRemediation())); // 原始请求/响应用CodeBlock样式 document.add(new Paragraph("原始请求:").setItalic()); document.add(new Paragraph(vuln.getRawRequest()).setFontColor(ColorConstants.GRAY)); } document.close(); }

设计说明:

  • simsun.ttc字体文件必须放在src/main/resources/fonts/下,否则PDF生成时抛IOException;
  • PdfFontFactory.createFont(..., true)的true参数表示启用字体子集嵌入,避免PDF体积暴增;
  • 所有中文文本必须通过document.add(new Paragraph(...))添加,直接document.add("中文")会乱码。

4. 常见问题排查:5个毕设答辩高频翻车点及根治方案

4.1 现象:扫描结果全是CONNECTION_TIMEOUT,但浏览器能正常访问目标网站

原因:Netty客户端未正确设置SO_TIMEOUT,或目标服务器启用了SYN Cookie导致三次握手延迟。更常见的是学生把targetUrl写成https://example.com却没在requestTemplate中配置SSLContext。
解决:在NetworkClientFactory.java的Bootstrap配置中,对HTTPS目标强制添加SSL处理器:

if (url.startsWith("https://")) { SslContext sslCtx = SslContextBuilder.forClient() .trustManager(InsecureTrustManagerFactory.INSTANCE) // 仅测试用!答辩时需替换为真实CA .build(); ch.pipeline().addFirst("ssl", sslCtx.newHandler(ch.alloc(), host, port)); }

提示:答辩时若被问“信任所有证书是否安全”,回答:“这是开发阶段的便捷配置,实际部署需集成企业CA证书,并在SslContextBuilder中指定trustManager(new File("ca.crt"))。”

4.2 现象:JSON规则文件修改后不生效,始终执行旧规则

原因:RuleLoader.java使用ClassLoader.getResourceAsStream("rules/")加载规则,但IDE中修改JSON后未触发资源热更新,或打包成JAR后路径变为jar:file:/xxx.jar!/rules/,File.listFiles()失效。
解决:改用ClassPathResource配合ResourcePatternResolver:

private List<Rule> loadAllRules() { ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); try { Resource[] resources = resolver.getResources("classpath*:rules/**/*.json"); List<Rule> rules = new ArrayList<>(); for (Resource resource : resources) { String json = StreamUtils.copyToString(resource.getInputStream(), StandardCharsets.UTF_8); rules.add(objectMapper.readValue(json, Rule.class)); } return rules; } catch (Exception e) { throw new RuntimeException("Failed to load rules", e); } }

关键点:classpath*:前缀支持JAR内资源扫描,**/*.json递归匹配子目录。

4.3 现象:PDF报告中中文显示为方框,HTML报告正常

原因:iText 7默认字体不支持中文,且PdfFontFactory.createFont()未指定字体名称(如"SimSun"),或字体文件路径错误。
解决:三步确认:

  1. 检查src/main/resources/fonts/simsun.ttc是否存在(Windows系统可直接用simsun.ttc,Linux/macOS需用NotoSansCJKsc-Regular.otf);
  2. createFont()第二个参数必须是字体内部名称,用fonttools命令查看:ttx -l simsun.ttc;
  3. 在PDF文档中显式设置字体:document.setFont(font),不能只在Paragraph中设置。

4.4 现象:多线程扫描时出现ConcurrentModificationException

原因:Vulnerability对象被多个线程同时写入rawRequest/rawResponse字段,而这些字段是String类型,但StringBuilder在append()时未同步。
解决:将Vulnerability改为不可变对象,所有字段final,构造时传入完整数据:

public class Vulnerability { private final String id; private final String name; private final String rawRequest; // 构造时一次性赋值 private final String rawResponse; public Vulnerability(String id, String name, String rawRequest, String rawResponse) { this.id = id; this.name = name; this.rawRequest = rawRequest; this.rawResponse = rawResponse; } // 移除所有setter方法 }

注意:答辩时强调“不可变对象天然线程安全,避免锁竞争,符合Java并发最佳实践”。

4.5 现象:扫描http://localhost:8080时抛java.net.ConnectException: Connection refused

原因:Netty默认使用NioSocketChannel,但localhost回环地址在某些Linux发行版上需用EpollSocketChannel。更可能是目标Tomcat未启动,或防火墙拦截。
解决:增加本地调试专用配置:

// 在application.properties中添加 scan.local-mode=true # ScannerService.java中 if (isLocalMode && target.getHost().equals("localhost")) { // 切换为Epoll(Linux)或KQueue(macOS)通道 bootstrap.channel(epollAvailable ? EpollSocketChannel.class : NioSocketChannel.class); }

血泪经验:答辩前务必用netstat -an | grep 8080确认端口监听状态,别让基础环境问题毁掉整个演示。


5. 毕设答辩验收指南:从演示脚本到答辩话术的全流程准备

5.1 演示脚本设计:3分钟精准呈现系统价值,避开所有技术深坑

答辩演示不是代码走读,而是用最小操作证明系统有效性。我给某高校A同学设计的脚本至今被沿用:

时间操作屏幕展示设计意图
0:00-0:20启动VulnScannerApp.java,输入http://demo.testfire.net(公开靶场)控制台输出[INFO] Starting scan for http://demo.testfire.net...证明系统可运行,目标合法
0:21-1:10打开rules/web/sql-injection.json,修改matchers[0].pattern为"mysql.*syntax",保存编辑器窗口特写+保存动画展示规则可配置性,非硬编码
1:11-1:50点击“开始扫描”,等待15秒后自动生成report/output/vuln-report.html浏览器打开HTML报告,高亮显示SQL注入漏洞条目证明检测能力,且结果可读
1:51-2:30右键报告页→“打印”→选择“另存为PDF”,打开生成的PDFPDF缩略图展示中文字体正常、章节清晰验证报告层完整性
2:31-3:00切换终端,执行java -jar vuln-scanner.jar --target https://example.com --rule-path rules/cve-2023-1234.json命令行输出Found vulnerability: CVE-2023-1234 (MEDIUM)证明CLI模式可用,满足部署需求

关键技巧:

  • 所有演示目标必须提前测试——demo.testfire.net是IBM提供的合法靶场,example.com确保无漏洞(作为负样本);
  • 修改规则后不重启程序,直接点击“重新加载规则”按钮(该功能在RuleManager.java中实现),体现热更新能力;
  • PDF导出环节故意放慢操作,让评委看清“字体嵌入”过程,这是Java毕设里少有人做到的细节。

5.2 答辩话术库:针对6类高频问题的标准应答模板

老师提问往往围绕“为什么”“怎么保证”“如果...怎么办”,提前准备应答能极大提升可信度:

问题类型典型问题标准应答(精简版)应答要点
技术选型“为什么用Netty不用OkHttp?”“OkHttp是HTTP客户端库,无法处理TCP层SYN扫描或自定义协议;而本系统需同时支持HTTP/HTTPS/DNS/FTP多种协议探测,Netty的ChannelPipeline可插拔设计允许我在同一套连接池上叠加不同协议编解码器。”绑定具体功能需求,不空谈“高性能”
安全性“信任所有SSL证书是否合规?”“开发阶段使用InsecureTrustManagerFactory仅为快速验证POC,实际部署时已在application-prod.yml中配置了ssl.trust-store: classpath:prod-ca.jks,并通过KeyStore.getInstance("JKS")加载企业CA。”区分开发/生产环境,给出具体配置路径
可扩展性“新增一个漏洞检测要改几处代码?”“只需3步:1. 在rules/下新建JSON文件(如log4j2.json);2. 在app.properties中添加rule.include=log4j2;3. 运行时自动加载,无需编译Java代码。”用数字量化工作量,强调JSON驱动优势
鲁棒性“目标网站返回503时系统如何处理?”“在PocExecutor.java的catch块中,我们捕获HttpStatusCodeException,将其转换为Vulnerability对象,severity设为INFO,description注明‘服务暂时不可用’,确保扫描流程不中断。”展示异常处理策略,而非回避问题
测试验证“怎么证明检测结果准确?”“所有POC均在Docker靶场中实测:用docker run -d -p 8080:8080 medicean/vulapps:s_struts2_s2-045启动S2-045靶机,系统成功检出并返回/opt/tomcat路径,与靶机实际路径一致。”给出可复现的验证步骤,附Docker命令
工程规范“代码有没有单元测试?”“每个核心模块均有JUnit 5测试:NetworkClientTest验证超时熔断,RuleLoaderTest测试JSON解析,PdfExporterTest检查中文字体嵌入。测试覆盖率报告在target/site/jacoco/下可查。”指出具体测试类名和报告路径,增强可信度

5.3 答辩材料清单:一份让导师眼前一亮的交付包

除了代码,一份专业的毕设交付包应包含5类材料,缺一不可:

材料类型文件名必备内容作用
系统架构图docs/architecture.pngPlantUML绘制的四层架构图,标注各层Java类名(如NetworkClientFactory→网络层)直观展示设计思想,避免文字描述苍白
规则样例集rules/examples/包含sql-injection.json、xss-reflected.json、cve-2023-1234.json三个真实漏洞规则,每个含description和remediation字段证明规则引擎实用性,非玩具数据
测试报告docs/test-report.pdfJUnit生成的覆盖率报告(含分支覆盖)、Docker靶场实测截图、性能压测数据(100并发平均耗时)用数据说话,体现工程严谨性
用户手册docs/user-guide.md含快速启动(3行命令)、规则编写规范(JSON Schema)、CLI参数说明(--threads,--timeout)展示产品化思维,不止于代码
答辩PPTpresentation.pptx严格遵循“问题→方案→实现→验证”逻辑链,每页不超过1张图+20字要点,禁用动画让导师3秒抓住重点,避免信息过载

提示:PPT中“总体架构”页必须放一张手绘风格的分层图(可用Excalidraw绘制),比Visio自动生成的图更有“人味”,导师会觉得你真理解了设计。

从那以后我每次带毕设,都强制学生在答辩前完成三件事:用Docker靶场跑通一个真实CVE检测、生成一份带中文字体的PDF报告、写出user-guide.md里“如何新增规则”的操作步骤。这三件事做完,80%的答辩问题都能自然化解——因为它们逼着学生从“写代码”转向“交付解决方案”。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询