Files
notes/work/移动杭研/业务梳理/信安信息/信安信息递归问题.md
T
2026-03-01 01:43:46 +08:00

197 lines
6.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
```
---
【核心判断】
🔴 这是垃圾代码 - 复杂、低效、有隐藏的递归炸弹
---
【流程分析:a.b.c.tttt.cn 每层都没备案会怎样】
假设主域名集合包含 cn,那么:
第1轮:直接查询
查询: a.b.c.tttt.cn
返回: false (未备案)
计算主域名: tttt.cn
生成子域名列表: [b.c.tttt.cn, c.tttt.cn, tttt.cn]
进入第2轮 ↓
第2轮:递归查子域名(第258行的递归陷阱)
查询: [b.c.tttt.cn, c.tttt.cn, tttt.cn]
全部返回: false
↓ 分别处理:
b.c.tttt.cn → 生成 [c.tttt.cn, tttt.cn]
c.tttt.cn → 生成 [tttt.cn]
tttt.cn → 生成 [] (空!递归终止条件)
合并后查询列表: [c.tttt.cn, tttt.cn, tttt.cn] ← 注意重复!
进入第3轮 ↓
第3轮:继续递归
查询: [c.tttt.cn, tttt.cn, tttt.cn] ← tttt.cn被查了2次!
全部返回: false
c.tttt.cn → 生成 [tttt.cn]
tttt.cn → 生成 []
tttt.cn → 生成 []
合并后查询列表: [tttt.cn]
进入第4轮 ↓
第4轮:最后一次
查询: [tttt.cn]
返回: false
tttt.cn → 生成 []
递归结束!
最终结果
icpMap = {
"a.b.c.tttt.cn": "false" // 186行的逻辑会用最后一次子域名查询结果覆盖
}
---
【致命问题】
1️⃣ 隐藏的递归炸弹 (XinAnApiService.java:258)
Map<String, String> domainsICP = getDomainsIcp(subDomains); // 递归!
这他妈是在干什么?
- 方法 getDomainsIcp 调用 getSubDomainIcp
- getSubDomainIcp 又调用回 getDomainsIcp
- 形成递归,但没有任何注释说明!
- 递归深度 = 域名层级数 - 1
对于 a.b.c.d.e.f.g.tttt.cn8层域名):
- 会递归 7 次
- 总查询次数:1 + 7 + 6 + 5 + 4 + 3 + 2 + 1 = 28 次调用信安接口!
- 而且 tttt.cn 会被重复查询 7 次!
2️⃣ 数据结构错了 - 没有去重!
List<String> subDomains = Lists.newArrayList();
subDomainMap.values().forEach(subDomains::addAll); // 256-257行:直接addAll,没去重!
tttt.cn 会在 subDomains 列表中出现多次,每次都要查询信安接口。
"Bad programmers worry about the code. Good programmers worry about data structures."
- 你需要的是 Set<String>,而不是 List<String>
- 或者在查询前去重:subDomains.stream().distinct().collect(Collectors.toList())
3️⃣ 复杂度爆炸 - 3层缩进是底线,这代码有多少层?
看看第147-158行的嵌套:
for (Map.Entry<String, String> entry : icpMap.entrySet()) { // 第1层
if (StringUtils.isEmpty(icp) || FALSE_ICP.getValue().equals(icp)) { // 第2层
if (CollectionUtils.isEmpty(subDomains)) { // 第3层
continue;
}
}
}
还有异常处理里又重复了一遍逻辑(159-170行)—— 这是什么鬼设计?
4️⃣ getSubDomain 的诡异逻辑 (XinAnApiService.java:358-367)
temp.forEach(var ->
subDomains.addFirst(StringUtils.join(Arrays.asList(var, subDomains.getFirst()), SEPARATOR2))
);
subDomains.remove(subDomains.getFirst()); // 为什么要先加再删?
为什么不一开始就不加原域名?
- 这是在解决不存在的问题
- 应该直接构建正确的列表,而不是"先加后删"
---
【Linus式重构方案】
核心思想:消除递归,用迭代 + 去重
public Map<String, String> getDomainsIcp(List<String> domains) {
if (CollectionUtils.isEmpty(domains)) {
return Maps.newHashMap();
}
// 第1步:收集所有需要查询的域名(包括子域名),用 Set 去重
Set<String> allDomainsToQuery = new LinkedHashSet<>();
Map<String, List<String>> domainHierarchy = new HashMap<>();
for (String domain : domains) {
allDomainsToQuery.add(domain);
String mainDomain = getDomainParam(domain);
List<String> subDomains = getSubDomain(mainDomain, domain);
allDomainsToQuery.addAll(subDomains); // Set自动去重
domainHierarchy.put(domain, subDomains);
}
// 第2步:一次性查询所有域名(去重后)
Map<String, String> allIcpResults = queryDomainsIcpByGroup(new ArrayList<>(allDomainsToQuery));
// 第3步:为每个原始域名找最近的有备案的父域名
Map<String, String> result = new HashMap<>();
for (String domain : domains) {
String icp = allIcpResults.get(domain);
// 如果自己有备案,直接用
if (icp != null && !icp.equals(FALSE_ICP.getValue()) && !icp.isEmpty()) {
result.put(domain, icp);
continue;
}
// 否则从子域名列表中找第一个有备案的
List<String> subDomains = domainHierarchy.get(domain);
boolean found = false;
if (subDomains != null) {
for (String subDomain : subDomains) {
String subIcp = allIcpResults.get(subDomain);
if (subIcp != null && !subIcp.equals(FALSE_ICP.getValue()) && !subIcp.isEmpty()) {
result.put(domain, subIcp);
found = true;
break;
}
}
}
if (!found) {
result.put(domain, icp != null ? icp : "");
}
}
return result;
}
改进效果:
| 指标 | 原代码 | 重构后 |
|--------------|---------------|-----------|
| 递归次数 | N层 | 0 |
| 接口调用次数(8层域名) | 28次 | 8次(去重后) |
| 重复查询 | 是 | 否 |
| 代码行数 | ~80行(分散在3个方法) | ~40行(单方法) |
| 可读性 | 需要追踪递归 | 一目了然 |
---
【最后警告】
这段代码有三个经典反模式:
1. "聪明"代码 - 递归隐藏在方法调用里,不看完整代码根本发现不了
2. 重复逻辑 - 异常处理块里复制粘贴了主逻辑
3. 过度抽象 - getSubDomain 方法可以简化成3行,但写成了10行
"Talk is cheap. Show me the code." - 现在代码给你了,你看着办吧。
需要我帮你重构吗?还是你有其他问题?
```