简介:本资源是一套面向计算机专业本科生的毕业设计实战项目,聚焦网络安全实践能力培养,提供一个基于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.15 | JSON规则文件解析速度快,支持注解绑定(@JsonProperty直映射到Rule对象) | XML解析冗余,且学生易在DTD声明上翻车 |
| Thymeleaf 3.1 | HTML报告模板语法简洁,th:each遍历漏洞列表比JSP标签直观得多 | FreeMarker学习成本高,Velocity已过时 |
| iText 7.2 | PDF导出支持中文宋体嵌入,避免答辩现场字体乱码 | Apache PDFBox对中文字体处理需额外配置 |
关键决策点:当老师问“为什么不用Spring Boot?”,标准答案是:“本系统核心是IO密集型扫描任务,Spring Boot的自动配置会引入大量无用Bean,内存占用增加40%,而Netty原生API对连接超时、SSL握手失败等异常的捕获粒度更细——比如SslHandshakeTimeoutException能精准定位到TLS版本不兼容,这在渗透测试复现中至关重要。”
2.3 核心数据流图:从URL输入到PDF报告的7个关键节点
整个扫描流程不是线性执行,而是事件驱动的状态机。以扫描http://test.com为例,数据流如下:
- 输入解析:
TargetParser.java将URL转为Target对象(含host/port/protocol/scheme),校验格式并标准化; - 资产发现:
NetworkDiscovery.java调用ICMP+TCP SYN探测,生成存活主机列表(非必须,但体现工程思维); - 服务识别:
ServiceDetector.java向80/443端口发送HEAD / HTTP/1.1,解析Server、X-Powered-By等Header; - 规则加载:
RuleLoader.java读取rules/web/目录下所有JSON,按appType(如apache-tomcat)和severity(HIGH/MEDIUM)分类缓存; - POC验证:
PocExecutor.java根据规则中的requestTemplate动态填充payload,用Netty Channel发送,截获原始二进制响应; - 结果匹配:
MatcherEngine.java执行多级匹配(正则提取+字符串包含+状态码校验+响应长度阈值),任一失败即终止该POC; - 报告聚合:
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"),或字体文件路径错误。
解决:三步确认:
- 检查
src/main/resources/fonts/simsun.ttc是否存在(Windows系统可直接用simsun.ttc,Linux/macOS需用NotoSansCJKsc-Regular.otf); createFont()第二个参数必须是字体内部名称,用fonttools命令查看:ttx -l simsun.ttc;- 在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”,打开生成的PDF | PDF缩略图展示中文字体正常、章节清晰 | 验证报告层完整性 |
| 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.png | PlantUML绘制的四层架构图,标注各层Java类名(如NetworkClientFactory→网络层) | 直观展示设计思想,避免文字描述苍白 |
| 规则样例集 | rules/examples/ | 包含sql-injection.json、xss-reflected.json、cve-2023-1234.json三个真实漏洞规则,每个含description和remediation字段 | 证明规则引擎实用性,非玩具数据 |
| 测试报告 | docs/test-report.pdf | JUnit生成的覆盖率报告(含分支覆盖)、Docker靶场实测截图、性能压测数据(100并发平均耗时) | 用数据说话,体现工程严谨性 |
| 用户手册 | docs/user-guide.md | 含快速启动(3行命令)、规则编写规范(JSON Schema)、CLI参数说明(--threads,--timeout) | 展示产品化思维,不止于代码 |
| 答辩PPT | presentation.pptx | 严格遵循“问题→方案→实现→验证”逻辑链,每页不超过1张图+20字要点,禁用动画 | 让导师3秒抓住重点,避免信息过载 |
提示:PPT中“总体架构”页必须放一张手绘风格的分层图(可用Excalidraw绘制),比Visio自动生成的图更有“人味”,导师会觉得你真理解了设计。
从那以后我每次带毕设,都强制学生在答辩前完成三件事:用Docker靶场跑通一个真实CVE检测、生成一份带中文字体的PDF报告、写出user-guide.md里“如何新增规则”的操作步骤。这三件事做完,80%的答辩问题都能自然化解——因为它们逼着学生从“写代码”转向“交付解决方案”。希望帮到你。
本文还有配套的精品资源,点击获取