1. 项目概述为什么Java系统安全漏洞检测与修复是每个开发者的必修课最近在帮几个朋友的公司做代码审计发现一个挺普遍的现象很多Java项目上线前安全测试环节要么是草草了事要么干脆就依赖运维在防火墙层面做点基础防护。结果呢轻则被爬虫薅羊毛重则数据泄露、服务被勒索上线没几个月就焦头烂额。这让我想起自己早年踩过的坑一个简单的SQL注入漏洞差点让整个用户数据库裸奔。所以今天我想抛开那些大而全的安全框架理论就围绕“检测”与“修复”这两个核心动作聊聊我们一线Java开发者手里真正能用、该用的七条实战秘籍。这七条秘籍不是什么高深莫测的黑科技而是从编码习惯、依赖管理、配置检查到运行时防护的一套组合拳。它们针对的正是那些最常见、最容易被忽视但破坏力却极强的漏洞类型比如依赖组件里的“暗雷”第三方库漏洞、配置不当留下的“后门”如不安全的反序列化、以及代码里自己埋下的“坑”如硬编码密钥、不安全的日志记录。掌握它们意味着你能在开发阶段就提前堵住大部分漏洞而不是等到被安全团队扫描出红色高危警报或者更糟——被黑产团伙实际攻击后才手忙脚乱地打补丁。无论你是正在应对面试中“如何保证系统安全”这类八股问题的求职者还是负责维护线上系统稳定性的资深工程师这套方法都能直接提升你的代码“免疫力”。2. 秘籍一依赖成分分析——揪出第三方库里的“定时炸弹”2.1 为什么你的项目依赖可能比你的代码更危险现代Java开发离开Spring Boot、MyBatis、Apache Commons这些优秀的第三方库几乎寸步难行。但便利的背后是巨大的风险你引入的每一个jar包都可能带着已知甚至未知的安全漏洞。这些漏洞就像埋在项目里的“定时炸弹”攻击者不需要攻破你的业务逻辑只需要找到你使用的某个存在漏洞的库版本就能长驱直入。比如前几年肆虐的Log4j2漏洞CVE-2021-44228影响范围之广教训之深刻足以让每个开发者警醒。问题的核心在于我们通常只关心依赖是否能编译通过、功能是否正常却很少持续关注这些依赖本身的安全性。手动跟踪所有依赖的CVE公共漏洞和暴露公告几乎是不可能的任务。因此自动化、持续化的依赖成分分析Software Composition Analysis, SCA必须成为开发流程的一部分。2.2 实战工具选型与集成Maven OWASP Dependency-Check对于Maven项目OWASP Dependency-Check是目前最主流、最易集成的开源SCA工具。它不仅仅是一个扫描工具更可以无缝集成到你的构建生命周期中。集成步骤与配置要点在pom.xml中集成插件build plugins plugin groupIdorg.owasp/groupId artifactIddependency-check-maven/artifactId version8.4.0/version !-- 请使用最新版本 -- executions execution goals goalcheck/goal /goals /execution /executions configuration !-- 关键配置设置严重性阈值只有高危及以上漏洞才导致构建失败 -- failBuildOnCVSS7/failBuildOnCVSS !-- 生成多种格式报告 -- formatsHTML, JSON, XML/formats !-- 跳过对某些scope的分析如测试依赖 -- skipTestScopetrue/skipTestScope !-- 配置本地漏洞数据库镜像加速扫描 -- dataMirrorhttps://mirror.你的公司内部域名//dataMirror /configuration /plugin /plugins /build执行扫描与分析报告 运行mvn dependency-check:check或直接mvn verify如果配置了execution。扫描完成后会在target目录下生成dependency-check-report.html文件。打开这个报告你会看到一张清晰的表格列出了所有存在已知CVE漏洞的依赖包括漏洞编号CVE-ID、严重等级CVSS分数、描述以及受影响的版本范围。实操心得与避坑指南不要盲目追求“零漏洞”有些历史悠久的库可能会报出几十个低危漏洞。你需要结合上下文判断风险。例如一个仅用于单元测试、且漏洞利用需要网络访问的库其实际风险极低。合理设置failBuildOnCVSS阈值如7.0只让高危和严重漏洞阻断构建是平衡安全与效率的关键。善用“抑制文件”Suppression File对于经评估确认为误报或可接受风险的漏洞可以创建一个XML格式的抑制文件避免每次扫描都报警。但务必记录抑制理由并定期复审。?xml version1.0 encodingUTF-8? suppressions xmlnshttps://jeremylong.github.io/DependencyCheck/dependency-suppression.1.3.xsd suppress notes![CDATA[该漏洞CVE-XXXX-XXXX仅影响组件的XXX功能本项目未使用该功能。]]/notes cveCVE-XXXX-XXXX/cve /suppress /suppressions集成到CI/CD流水线这是发挥其最大价值的地方。在Jenkins、GitLab CI等工具中将依赖检查作为代码合并Merge Request或构建Build的一个必须通过的关卡。这样带有已知高危漏洞的依赖就无法被合并到主分支或生成部署包。3. 秘籍二静态应用安全测试——让代码在编译前“自曝其短”3.1 SAST的核心价值在漏洞产生之初就消灭它如果说依赖检查是防“外患”那么静态应用安全测试SAST就是查“内忧”。它直接在源代码级别进行分析无需运行程序就能发现编码过程中引入的安全缺陷如SQL注入、跨站脚本XSS、路径遍历、不安全的反序列化等。它的最大优势是“左移”将安全检测尽可能提前到开发阶段修复成本最低。3.2 主流SAST工具深度对比与落地实践市面上SAST工具很多从商业版的Fortify、Checkmarx到开源翘楚SpotBugsFind Security Bugs插件、SonarQube各有优劣。对于大多数团队我推荐SonarQube SpotBugs的组合拳兼顾了可持续性、易用性和深度。SonarQube 集成与规则定制SonarQube不仅是一个代码质量平台其安全规则库依托于OWASP Top 10和CWE也非常强大。通过SonarScanner集成到构建中。配置SonarQube服务器与项目首先搭建或使用现有的SonarQube服务器。在项目中创建sonar-project.properties文件。sonar.projectKeymy-java-app sonar.projectNameMy Java Application sonar.projectVersion1.0 sonar.sourcessrc/main/java sonar.testssrc/test/java sonar.java.binariestarget/classes sonar.java.librariestarget/**/*.jar # 关键指定语言和规则集 sonar.languagejava sonar.java.spotbugs.effortMax # 让SpotBugs插件更尽力地找问题执行扫描运行mvn clean verify sonar:sonar -Dsonar.loginyour_token。扫描结果会推送至SonarQube服务器在Web界面提供可视化报告。SpotBugs (Find Security Bugs) 的精准打击SpotBugs的Find Security Bugs插件在某些特定类型漏洞的检测上更加精准和深入尤其擅长字节码分析。在pom.xml中集成plugin groupIdcom.github.spotbugs/groupId artifactIdspotbugs-maven-plugin/artifactId version4.7.3.0/version configuration effortMax/effort thresholdLow/threshold !-- 连低级别问题也报告 -- plugins plugin groupIdcom.h3xstream.findsecbugs/groupId artifactIdfindsecbugs-plugin/artifactId version1.12.0/version /plugin /plugins /configuration executions execution goalsgoalcheck/goal/goals /execution /executions /plugin解读报告与修复案例 运行mvn spotbugs:check后如果发现问题构建会失败并在target/spotbugs.xml中生成详细报告。例如它可能会报告一个SQL_INJECTION_JDBC问题。问题代码String sql SELECT * FROM users WHERE username userInput ; Statement stmt connection.createStatement(); ResultSet rs stmt.executeQuery(sql); // 高危直接拼接用户输入修复方案必须使用预编译语句PreparedStatement。String sql SELECT * FROM users WHERE username ?; PreparedStatement pstmt connection.prepareStatement(sql); pstmt.setString(1, userInput); // 参数化查询安全 ResultSet rs pstmt.executeQuery();注意事项误报管理SAST工具不可避免会有误报。对于确认为误报的代码可以使用SuppressFBWarnings注解SpotBugs或在SonarQube界面上标记为“不会修复”并注明原因。但同样需要严格审批和记录。技术债看板将扫描出的安全漏洞特别是SonarQube中的纳入团队的技术债务看板设定修复SLA服务等级协议如“严重漏洞24小时内修复高危漏洞一周内修复”并定期回顾。与代码评审结合将SAST报告作为代码评审Code Review的必备参考资料。评审者不仅看功能实现更要关注安全扫描结果是否已处理。4. 秘籍三动态应用安全测试——以攻击者视角审视运行中的应用4.1 DAST模拟黑盒攻击验证运行时防护是否生效动态应用安全测试DAST是在应用程序运行时从外部模拟黑客攻击行为进行测试。它不关心内部代码如何实现只关心发送特定输入后应用会返回什么。这对于检测配置错误、身份认证和会话管理缺陷、以及SAST难以发现的逻辑漏洞至关重要。简单说SAST告诉你“代码哪里写错了”DAST告诉你“运行起来的程序哪里能被攻破”。4.2 手把手搭建自动化DAST扫描流水线对于Java Web应用OWASP ZAPZed Attack Proxy是一款功能强大且开源的黑盒扫描利器非常适合集成到CI/CD中。本地扫描与手动探索启动ZAP并配置代理下载ZAP启动后设置一个本地代理如localhost:8080。配置浏览器代理将你的浏览器或HTTP客户端如Postman的代理设置为ZAP的地址和端口。手动探索应用通过配置了代理的浏览器正常登录、浏览你的Web应用的所有功能。ZAP会自动记录下所有的HTTP请求和响应。发起主动扫描在ZAP的“站点”树上右键点击你的应用域名选择“攻击” - “主动扫描”。ZAP会根据爬取到的链接使用内置的数千条攻击规则进行自动化测试。自动化集成到CI/CD以Jenkins Pipeline为例要实现“每次部署前自动扫描”需要以“无头”headless模式运行ZAP。编写ZAP自动化扫描脚本(zap_scan.sh)#!/bin/bash # 启动ZAP守护进程 /path/to/zap.sh -daemon -port 8090 -host 0.0.0.0 -config api.disablekeytrue ZAP_PID$! echo “ZAP启动PID: $ZAP_PID” sleep 30 # 等待ZAP完全启动 # 定义目标应用URL TARGET_URL“http://your-test-env-app:8080” # 使用ZAP API进行快速扫描 curl “http://localhost:8090/JSON/ascan/action/scan/?url${TARGET_URL}recursetrueinScopeOnlyfalsescanPolicyNamemethodpostDatacontextId” # 等待扫描完成这里简化处理实际应轮询状态 sleep 300 # 生成HTML报告 curl “http://localhost:8090/OTHER/core/other/htmlreport/” zap_report.html # 检查是否有高风险漏洞根据报告内容简单grep生产环境应解析XML/JSON报告 if grep -q “High” zap_report.html; then echo “发现高风险漏洞构建失败” kill $ZAP_PID exit 1 # 使Jenkins构建失败 fi echo “DAST扫描通过未发现高风险漏洞。” kill $ZAP_PID集成到Jenkinsfilepipeline { agent any stages { stage(‘Build’) { steps { sh ‘mvn clean package’ } } stage(‘Deploy to Test’) { steps { // 将应用部署到测试环境 sh ‘kubectl apply -f k8s-deployment-test.yaml’ } } stage(‘DAST Security Scan’) { steps { // 运行自动化ZAP扫描脚本 sh ‘./scripts/zap_scan.sh’ } } stage(‘Deploy to Prod’) { when { expression { currentBuild.result null || currentBuild.result ‘SUCCESS’ } } steps { // 只有DAST扫描通过才部署到生产 sh ‘kubectl apply -f k8s-deployment-prod.yaml’ } } } }核心注意事项扫描范围与身份认证确保ZAP爬虫能访问到需要认证的页面。可以通过ZAP的“上下文”Context功能导入登录后的会话Cookie或设置认证脚本。避免对生产环境扫描DAST是攻击性测试务必在独立的测试/预发环境进行严禁直接扫描生产系统可能导致服务不可用或数据污染。误报与漏报DAST的误报率可能较高需要人工确认。同时它无法覆盖所有路径漏报。因此DAST应是安全测试体系的一部分而非全部。扫描策略调优ZAP默认策略可能过于激进或温和。应根据应用特点自定义扫描策略例如对某些敏感API端点降低攻击强度或对某些目录排除扫描。5. 秘籍四安全配置审查——堵住默认配置留下的“后门”5.1 框架与中间件的“安全基线”很多安全漏洞并非源于业务代码而是由于框架、应用服务器或中间件的不安全默认配置或不当配置。Spring Boot以其“约定优于配置”闻名但如果不了解这些约定背后的安全含义就会留下隐患。5.2 Spring Boot安全配置关键点清单与修复以下是一份必须检查的Spring Boot安全配置清单执行器端点Actuator暴露风险/actuator端点如/actuator/env,/actuator/heapdump暴露了大量敏感信息和操作接口。修复在生产环境中通过management.endpoints.web.exposure.include和exclude属性严格控制暴露的端点。通常只保留health和info。# application-prod.yml management: endpoints: web: exposure: include: health,info endpoint: health: show-details: never # 健康检查详情不对外显示进阶为Actuator端点配置独立的端口和基于IP的访问控制或通过Spring Security进行严格的权限校验。Swagger/OpenAPI文档暴露风险开发阶段用于测试的Swagger UI (/swagger-ui.html,/v3/api-docs) 如果暴露在生产环境会泄露所有API接口信息成为攻击者的“地图”。修复使用Profile控制。Configuration Profile(“!prod”) // 仅在非生产环境生效 public class SwaggerConfig { Bean public Docket api() { return new Docket(DocumentationType.OAS_30) .select() .apis(RequestHandlerSelectors.any()) .paths(PathSelectors.any()) .build(); } }敏感配置信息风险数据库密码、API密钥、加密盐值等硬编码在application.properties或提交到代码库。修复原则永远不要将敏感信息提交到版本控制系统。方法1推荐使用环境变量或云平台/容器的密钥管理服务如K8s Secrets, AWS Secrets Manager。spring: datasource: url: ${DB_URL} username: ${DB_USER} password: ${DB_PASSWORD} # 从环境变量注入方法2使用Jasypt等库对配置文件中的敏感信息进行加密但密钥本身仍需安全管理。HTTP安全头缺失风险缺少关键安全头如Content-Security-Policy(CSP),X-Content-Type-Options,X-Frame-Options,Strict-Transport-Security(HSTS)易遭受XSS、点击劫持等攻击。修复Spring Security可以方便地添加这些头。Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter { Override protected void configure(HttpSecurity http) throws Exception { http .headers() .contentSecurityPolicy(“script-src ‘self’;”) // 示例CSP .and() .frameOptions().deny() // 禁止被嵌入iframe .and() .httpStrictTransportSecurity() .includeSubDomains(true) .maxAgeInSeconds(31536000); // 强制HTTPS } }不安全的反序列化风险使用ObjectInputStream反序列化不可信数据如RPC通信、缓存数据可能导致远程代码执行RCE。修复首选使用JSONJackson/Gson等更安全的序列化协议替代Java原生序列化。如果必须用使用ObjectInputFilter(Java 9) 或第三方库如Apache Commons IO的ValidatingObjectInputStream严格限制允许反序列化的类白名单。ObjectInputFilter filter ObjectInputFilter.Config.createFilter(“com.yourcompany.model.*;!*”); ObjectInputStream ois new ObjectInputStream(inputStream); ois.setObjectInputFilter(filter);配置审查自动化可以考虑使用像Chef InSpec、CIS Benchmarks这样的合规性即代码工具或编写自定义脚本定期对服务器、容器镜像的配置进行自动化检查确保符合安全基线。6. 秘籍五运行时应用自保护——为应用穿上“金钟罩”6.1 RASP的原理与价值从“边界防护”到“贴身防御”传统的WAFWeb应用防火墙部署在应用外围像城墙主要基于规则过滤请求。而运行时应用自保护RASP则是将安全防护能力像“免疫系统”一样注入到应用运行时内部。它能够监控应用自身的执行流、上下文和内存当检测到恶意行为如SQL注入、命令执行、文件包含时可以实时阻断并告警。RASP的优势在于它能理解应用逻辑误报率低且能防护WAF难以应对的0day漏洞和逻辑漏洞。6.2 开源RASP工具选型与实践以OpenRASP为例对于Java应用百度开源的OpenRASP是一个成熟的选择。它通过Java Agent技术无侵入式地植入应用。部署与配置步骤下载与安装从GitHub Release页面下载OpenRASP的Java Agent包 (rasp-java.tar.gz)。启动应用时加载Agent通过JVM参数加载。java -javaagent:/path/to/rasp/rasp.jar -Drasp.app_idyour_app_name -Drasp.installcloud -jar your-application.jar-Drasp.installcloud表示连接到云控中心需单独部署方便集中管理策略和查看日志。也可以使用-Drasp.installlocal进行本地模式部署。配置防护策略OpenRASP的核心是其插件系统。你可以通过云控中心或本地配置文件 (plugins/) 来管理防护规则。例如默认的plugin.js文件就包含了SQL注入、命令执行、文件读取等检测逻辑。你可以根据应用实际情况调整规则的敏感度如是否拦截、仅记录日志和检测算法。验证与监控验证部署后尝试发起一个简单的SQL注入攻击http://yourapp/user?id1 OR 11。如果配置了拦截模式请求会被阻断并返回一个包含X-Protected-By: OpenRASP头的错误页面。监控所有安全事件拦截或报警都会记录到日志文件或发送到云控中心。你需要将这些日志接入ELK或Splunk等日志平台并设置告警。实操心得性能影响评估RASP由于需要Hook关键函数会对性能产生一定影响通常5%。在性能敏感的核心接口上可以通过配置对特定路径或参数名关闭某些检测进行精细化调优。策略灰度与学习初期建议将策略设置为“仅记录日志”模式运行一段时间分析日志中的误报和漏报。根据实际流量“学习”和调整规则后再逐步开启拦截模式。不是银弹RASP不能替代代码安全、依赖安全和配置安全。它是一道重要的运行时防线用于检测和阻断那些逃过了前面几道关卡的攻击尝试。应与WAF、SAST、DAST等共同构成纵深防御体系。7. 秘籍六安全编码规范与代码审查——将安全融入开发DNA7.1 建立团队专属的安全编码清单工具是辅助人才是根本。再好的扫描工具也抵不过开发人员一个危险的操作。建立并强制执行团队的安全编码规范至关重要。这份清单不应是网上随便找来的长篇大论而应是结合团队技术栈和业务特点的“军规”。一份精简的Java安全编码清单示例输入验证必须对所有来自外部的输入HTTP请求参数、Header、Cookie、文件上传、RPC参数进行严格的校验类型、长度、范围、格式。使用Bean Validation (NotNull,Size,Pattern) 或白名单校验。禁止直接将用户输入用于拼接SQL、操作系统命令、日志输出、反射类名、文件路径。输出编码必须根据输出上下文进行编码。在HTML页面中渲染用户数据使用HTML实体编码如lt;转义在JavaScript中使用JSON序列化在SQL中使用参数化查询。工具使用ESAPI、OWASP Java Encoder等库辅助编码。身份认证与会话管理必须使用强密码策略、多因素认证MFA。会话ID必须随机、足够长并在登出和超时后立即失效。使用安全的Cookie属性HttpOnly,Secure,SameSite。框架优先使用Spring Security等成熟框架避免自己造轮子。敏感数据处理必须密码、密钥等敏感信息在存储时必须加盐哈希如使用BCrypt。在内存中使用后尽快清除如填充\0。日志禁止在日志中记录密码、信用卡号、身份证号等完整敏感信息。使用脱敏工具。错误处理必须向用户返回通用的错误信息如“系统内部错误”避免将堆栈跟踪、SQL语句、服务器路径等详细信息暴露给客户端。方式使用全局异常处理器ControllerAdvice统一处理。7.2 将安全审查嵌入代码评审流程规范写在纸上没用必须融入流程。最有效的方式就是将安全作为代码评审Code Review的强制检查项。具体操作在Pull Request模板中增加安全清单在GitLab、GitHub等平台的PR模板里加入一个安全检查章节要求作者和评审者确认。## 安全检查 - [ ] 本次变更是否引入了新的用户输入点是否已进行校验和编码 - [ ] 是否涉及数据库操作是否使用参数化查询或ORM框架的安全方法 - [ ] 是否处理敏感数据密码、密钥、PII存储和传输是否安全 - [ ] 是否修改了安全相关配置如Spring Security, CORS, Actuator - [ ] 是否引入了新的第三方依赖是否已用Dependency-Check扫描 - [ ] 本次变更是否可以通过已有的SAST/DAST扫描设立“安全评审员”角色在团队中指定或轮流担任安全评审员对涉及核心安全功能如登录、支付、权限的PR进行重点审查。利用自动化工具辅助评审将SAST工具如SonarQube质量门禁与代码平台集成要求PR合并前必须通过安全扫描并将扫描结果直接评论到PR中作为评审依据。经验之谈安全代码评审初期可能会拖慢开发速度但形成习惯后会成为开发者的肌肉记忆。关键在于“小步快跑持续教育”通过每次评审中的具体案例让团队成员直观地理解安全漏洞的危害和修复方法比任何培训都有效。8. 秘籍七漏洞修复与应急响应——从“救火”到“防火”的闭环8.1 建立漏洞分级与响应机制漏洞被发现后混乱的响应比漏洞本身更可怕。必须建立清晰的漏洞管理流程Vulnerability Management Process, VMP。漏洞分级根据CVSS分数、利用难度、影响范围数据、功能、系统、是否存在公开EXP漏洞利用代码等因素将漏洞分为严重需立即停止手头工作2小时内开始修复24小时内完成修复和上线。高危需在下一个工作日开始修复设定明确的修复截止日期如3天。中危/低危纳入技术债务在常规迭代中规划修复。建立响应小组明确漏洞接收人通常是安全团队或技术负责人、评估人、修复负责人开发Owner、验证人测试/安全和发布人运维。工具支持使用Jira、Confluence或专门的漏洞管理平台来跟踪漏洞的生命周期发现-评估-分配-修复-验证-关闭。8.2 修复实操以Log4j2漏洞CVE-2021-44228为例当收到一个类似Log4j2这样的紧急漏洞警报时一个有序的修复流程是怎样的第一步确认与评估1小时内确认影响快速在项目中搜索log4j-core依赖确认使用的版本是否在受影响范围2.0-beta9 到 2.14.1。mvn dependency:tree | grep log4j-core # 或 gradle dependencies | grep log4j-core评估风险确认应用是否对外暴露了日志输入接口如日志收集的HTTP接口评估被利用的可能性。检查服务器和容器基础镜像是否包含漏洞组件。第二步制定修复方案立即首选方案升级升级到安全版本Log4j 2.15.0及以上。修改pom.xml或build.gradle。dependency groupIdorg.apache.logging.log4j/groupId artifactIdlog4j-core/artifactId version2.17.1/version !-- 使用当时最新的安全版本 -- /dependency临时缓解方案如果无法立即升级JVM参数启动应用时添加-Dlog4j2.formatMsgNoLookupstrue。系统环境变量设置LOG4J_FORMAT_MSG_NO_LOOKUPStrue。移除漏洞类从log4j-core的jar包中删除JndiLookup类。zip -q -d log4j-core-*.jar org/apache/logging/log4j/core/lookup/JndiLookup.class注意缓解方案只是临时措施必须尽快安排升级。第三步修复与测试根据分级时限创建热修复分支从生产分支拉取一个热修复分支。执行修复实施选定的修复方案如升级版本。全面测试功能回归确保升级不破坏现有功能。安全验证使用漏洞验证POC概念验证代码测试修复是否有效。对于Log4j2可以尝试构造一个恶意的日志消息看是否还能触发JNDI远程加载。依赖传递检查使用mvn dependency:tree再次确认所有传递依赖引入的log4j版本也已更新。第四步发布与监控紧接测试后制定回滚计划准备好旧版本的应用包和配置一旦新版本出现问题立即回滚。灰度发布先发布到一小部分服务器或用户观察监控指标错误日志、CPU/内存、异常请求是否正常。全量发布与监控确认无误后全量发布并持续监控安全告警平台和日志确保没有遗漏的攻击尝试。第五步复盘与改进事后召开复盘会分析漏洞为何会引入为何使用了旧版本依赖检查是否失效响应流程哪些环节可以优化评估是否够快修复是否顺畅。更新流程与工具例如将此次漏洞的组件加入依赖扫描的黑名单或强制升级策略优化CI/CD流水线确保紧急发布通道畅通。这套“检测-评估-修复-验证-复盘”的闭环其意义远不止于修复一个具体漏洞。它迫使团队建立安全意识和规范流程将安全的“防火墙”从运维后置真正前置到开发、测试和发布的每一个环节。最终的目标是让安全从一项“额外任务”变成开发流程中自然而然、不可或缺的一部分。