diff --git a/.kiro/specs/amt-device-addition/requirements.md b/.kiro/specs/amt-device-addition/requirements.md new file mode 100644 index 0000000..e69de29 diff --git a/AMT_DEVICE_ADD_FEATURE.md b/AMT_DEVICE_ADD_FEATURE.md new file mode 100644 index 0000000..ad68464 --- /dev/null +++ b/AMT_DEVICE_ADD_FEATURE.md @@ -0,0 +1,245 @@ +# AMT 设备添加功能说明 + +## 功能概述 + +在设备管理的设备列表页面,增强了"新增设备"功能,支持通过 Intel AMT(Active Management Technology)协议自动发现和添加设备。 + +## 功能特性 + +### 1. 两种添加方式 + +#### 手动添加 +- 传统的手动输入设备信息方式 +- 需要手动填写设备名称、UUID、IP地址、MAC地址等信息 + +#### AMT 自动添加 +- 通过 AMT 协议自动发现设备 +- 只需输入 IP 地址和认证信息 +- 自动获取设备名称、MAC 地址等信息 +- 自动生成 UUID + +### 2. 认证方式 + +#### 直接输入 +- 手动输入 AMT 用户名和密码 +- 适合临时测试或一次性添加 + +#### 使用已保存凭证 +- 从"AMT 设置"页面选择已保存的凭证 +- 支持默认凭证快速选择 +- 提高安全性和便捷性 + +### 3. 连接测试 + +- 提供"测试连接"按钮 +- 验证 AMT 连接是否可用 +- 测试成功后自动获取设备信息 +- 显示连接状态反馈 + +## 技术实现 + +### 后端实现 + +#### 1. AmtDigestService.java +- 使用 WS-Management 协议与 AMT 设备通信 +- 支持 Digest 认证(HTTP Digest Auth) +- 实现设备信息获取和连接测试 + +#### 2. 主要接口 + +**测试 AMT 连接** +``` +POST /device/amt/test +{ + "ipAddress": "192.168.1.100", + "username": "admin", + "password": "password" +} +或 +{ + "ipAddress": "192.168.1.100", + "credentialId": 1 +} +``` + +**获取 AMT 设备信息** +``` +POST /device/amt/getInfo +{ + "ipAddress": "192.168.1.100", + "username": "admin", + "password": "password" +} +``` + +#### 3. WS-Management 协议 + +使用标准的 SOAP over HTTP 协议: +- 端口:HTTP 16992, HTTPS 16993 +- 认证:HTTP Digest Authentication +- 协议:WS-Management (WSMAN) + +### 前端实现 + +#### 1. 设备列表页面增强 +- 添加方式选择(单选按钮) +- AMT 设备发现表单 +- 认证方式切换(开关) +- 凭证选择下拉框 +- 测试连接按钮和状态显示 + +#### 2. 用户交互流程 + +1. 点击"新增设备"按钮 +2. 选择"AMT 自动添加"方式 +3. 输入 IP 地址 +4. 选择认证方式: + - 手动输入:填写用户名和密码 + - 使用凭证:从下拉框选择已保存的凭证 +5. 点击"测试连接" +6. 连接成功后,自动填充设备信息 +7. 确认并保存设备 + +## 使用说明 + +### 前置条件 + +1. AMT 设备已启用并配置 +2. AMT 设备网络可达 +3. 已知 AMT 管理员账号密码 +4. (可选)在"AMT 设置"中预先保存凭证 + +### 操作步骤 + +1. **进入设备列表页面** + - 导航到:设备管理 > 设备列表 + +2. **点击新增设备** + - 点击页面右上角的"新增设备"按钮 + +3. **选择 AMT 自动添加** + - 在弹出的对话框中选择"AMT 自动添加"单选按钮 + +4. **输入 AMT 设备信息** + - IP 地址:输入 AMT 设备的 IP 地址 + - 认证方式: + - 方式一:切换到"手动输入",填写用户名和密码 + - 方式二:切换到"使用已保存凭证",从下拉框选择凭证 + +5. **测试连接** + - 点击"测试连接"按钮 + - 等待测试结果 + - 成功后会显示绿色的"连接成功"标签 + +6. **确认设备信息** + - 系统自动填充设备名称、UUID、IP地址、MAC地址 + - 可以修改设备名称和备注 + - 选择设备状态 + +7. **保存设备** + - 点击"确定"按钮保存设备 + +### 注意事项 + +1. **网络连接** + - 确保服务器能够访问 AMT 设备的 16992 端口 + - 检查防火墙设置 + +2. **AMT 配置** + - AMT 设备必须已启用并配置 + - 确认 AMT 管理员账号可用 + +3. **安全性** + - 建议使用已保存的凭证而不是每次手动输入 + - 定期更新 AMT 密码 + +4. **错误处理** + - 连接失败时会显示具体错误信息 + - 常见错误: + - 网络不可达 + - 认证失败 + - AMT 未启用 + +## 文件清单 + +### 后端文件 +- `backend/src/main/java/com/soybean/admin/service/AmtDigestService.java` - AMT Digest 认证服务 +- `backend/src/main/java/com/soybean/admin/dto/AmtTestRequest.java` - 测试请求 DTO +- `backend/src/main/java/com/soybean/admin/dto/AmtDeviceInfo.java` - 设备信息 DTO +- `backend/src/main/java/com/soybean/admin/controller/DeviceController.java` - 设备控制器(已更新) + +### 前端文件 +- `src/views/device/list/index.vue` - 设备列表页面(已更新) +- `src/service/api/device.ts` - 设备 API(已更新) +- `src/typings/api/device.d.ts` - 设备类型定义(已更新) + +### 工具文件 +- `rebuild_and_test_amt.bat` - 重新编译和测试脚本 +- `AMT_DEVICE_ADD_FEATURE.md` - 本说明文档 + +## 测试建议 + +1. **单元测试** + - 测试 AMT 连接功能 + - 测试设备信息获取 + - 测试凭证集成 + +2. **集成测试** + - 使用真实 AMT 设备测试 + - 测试不同的认证方式 + - 测试错误处理 + +3. **用户测试** + - 测试完整的添加流程 + - 验证用户体验 + - 收集反馈 + +## 未来改进 + +1. **批量发现** + - 支持 IP 段扫描 + - 批量添加多个设备 + +2. **更多设备信息** + - 获取 CPU、内存等硬件信息 + - 获取 AMT 版本信息 + - 获取设备序列号 + +3. **高级功能** + - 支持 TLS 加密连接 + - 支持证书认证 + - 支持 Kerberos 认证 + +4. **监控集成** + - 定期检查设备状态 + - AMT 事件日志收集 + - 设备健康度监控 + +## 故障排除 + +### 问题:连接超时 +**原因**:网络不可达或防火墙阻止 +**解决**: +- 检查网络连接 +- 确认防火墙规则 +- 验证 AMT 端口 16992 是否开放 + +### 问题:认证失败 +**原因**:用户名或密码错误 +**解决**: +- 确认 AMT 管理员账号 +- 重置 AMT 密码 +- 检查凭证配置 + +### 问题:无法获取设备信息 +**原因**:AMT 未完全配置或版本不兼容 +**解决**: +- 检查 AMT 配置状态 +- 确认 AMT 版本 +- 查看 AMT 日志 + +## 参考资料 + +- [Intel AMT SDK Documentation](https://software.intel.com/sites/manageability/) +- [WS-Management Protocol](https://www.dmtf.org/standards/ws-man) +- [Intel vPro Technology](https://www.intel.com/content/www/us/en/architecture-and-technology/vpro/vpro-technology-general.html) diff --git a/AMT_NO_LOGS_FIX.md b/AMT_NO_LOGS_FIX.md new file mode 100644 index 0000000..e1260bd --- /dev/null +++ b/AMT_NO_LOGS_FIX.md @@ -0,0 +1,221 @@ +# AMT 功能无日志问题修复指南 + +## 问题描述 + +点击"测试连接"后,后端没有任何日志输出,直接显示超时错误。 + +## 可能原因 + +1. **代码未编译**:新添加的代码没有被编译 +2. **后端未重启**:修改后没有重启后端服务 +3. **请求未到达后端**:前端请求没有正确发送到后端 +4. **端口问题**:后端端口被占用或配置错误 + +## 解决步骤 + +### 步骤 1:停止当前后端服务 + +如果后端正在运行,先停止它: +- 在运行后端的命令行窗口按 `Ctrl+C` +- 或者关闭运行后端的窗口 + +### 步骤 2:重新编译并启动后端 + +运行以下脚本: + +```batch +rebuild_backend_with_logs.bat +``` + +这个脚本会: +1. 清理旧的编译文件 +2. 重新编译所有代码 +3. 启动后端服务并显示详细日志 + +### 步骤 3:验证后端是否正常运行 + +在新的命令行窗口运行: + +```batch +test_amt_api.bat +``` + +这个脚本会测试: +1. 后端是否运行 +2. AMT API 是否可访问 +3. 模拟模式状态 + +### 步骤 4:查看日志输出 + +启动后端后,你应该能看到类似的日志: + +``` +2024-xx-xx xx:xx:xx.xxx INFO xxxxx --- [main] c.s.a.Application : Starting Application +2024-xx-xx xx:xx:xx.xxx INFO xxxxx --- [main] c.s.a.Application : Started Application in x.xxx seconds +``` + +当你点击"测试连接"时,应该看到: + +``` +2024-xx-xx xx:xx:xx.xxx INFO xxxxx --- [http-nio-8080-exec-1] c.s.a.c.DeviceController : 收到 AMT 测试连接请求,IP: 192.168.1.100, 模拟模式: false +2024-xx-xx xx:xx:xx.xxx INFO xxxxx --- [http-nio-8080-exec-1] c.s.a.c.DeviceController : 使用真实 AMT 服务测试连接 +2024-xx-xx xx:xx:xx.xxx INFO xxxxx --- [http-nio-8080-exec-1] c.s.a.s.AmtService : 开始测试 AMT 连接,IP: 192.168.1.100 +2024-xx-xx xx:xx:xx.xxx INFO xxxxx --- [http-nio-8080-exec-1] c.s.a.s.AmtService : 使用手动输入的凭证,用户名: admin +2024-xx-xx xx:xx:xx.xxx INFO xxxxx --- [http-nio-8080-exec-1] c.s.a.s.AmtService : 准备发送 WS-Management 请求到: 192.168.1.100 +``` + +### 步骤 5:如果仍然没有日志 + +#### 5.1 检查后端是否真的在运行 + +```batch +netstat -ano | findstr 8080 +``` + +应该看到端口 8080 被占用。 + +#### 5.2 检查前端请求是否发送 + +打开浏览器开发者工具(F12),切换到 Network 标签,点击"测试连接",查看是否有请求发送到 `/device/amt/test`。 + +#### 5.3 检查请求 URL + +确认前端请求的 URL 是: +``` +POST http://localhost:8080/device/amt/test +``` + +#### 5.4 检查 CORS 问题 + +如果看到 CORS 错误,需要在后端添加 CORS 配置。 + +### 步骤 6:使用模拟模式测试 + +如果真实 AMT 连接有问题,先用模拟模式测试功能: + +#### 方法 1:修改代码 + +编辑 `backend/src/main/java/com/soybean/admin/controller/DeviceController.java`: + +```java +// 将这一行 +private boolean useMockMode = false; + +// 改为 +private boolean useMockMode = true; +``` + +然后重新编译并启动。 + +#### 方法 2:使用 API 切换 + +发送 POST 请求: + +```batch +curl -X POST http://localhost:8080/device/amt/toggleMock +``` + +或者在浏览器中访问: +``` +http://localhost:8080/device/amt/toggleMock +``` + +### 步骤 7:测试模拟模式 + +启用模拟模式后: +1. IP 地址:任意(如 192.168.1.100) +2. 用户名:`admin` +3. 密码:`admin` +4. 点击"测试连接" + +应该会成功并显示日志: + +``` +2024-xx-xx xx:xx:xx.xxx INFO xxxxx --- [http-nio-8080-exec-1] c.s.a.c.DeviceController : 收到 AMT 测试连接请求,IP: 192.168.1.100, 模拟模式: true +2024-xx-xx xx:xx:xx.xxx INFO xxxxx --- [http-nio-8080-exec-1] c.s.a.c.DeviceController : 使用模拟模式测试连接 +2024-xx-xx xx:xx:xx.xxx INFO xxxxx --- [http-nio-8080-exec-1] c.s.a.c.DeviceController : AMT 测试连接结果: true +``` + +## 常见问题 + +### Q1: 编译失败 + +**错误**:`mvn clean compile` 失败 + +**解决**: +1. 检查 Java 版本:`java -version`(需要 Java 8 或更高) +2. 检查 Maven 版本:`mvn -version` +3. 查看错误信息,可能缺少依赖 + +### Q2: 端口被占用 + +**错误**:`Port 8080 was already in use` + +**解决**: +1. 找到占用端口的进程: + ```batch + netstat -ano | findstr 8080 + ``` +2. 结束进程: + ```batch + taskkill /PID <进程ID> /F + ``` +3. 或者修改端口(在 `application.properties` 中) + +### Q3: 前端请求超时 + +**错误**:前端显示 `timeout of 10000ms exceeded` + +**原因**: +- 后端未运行 +- 后端处理请求时间过长 +- 网络问题 + +**解决**: +1. 确认后端正在运行 +2. 查看后端日志是否有错误 +3. 使用模拟模式测试 + +### Q4: 看不到详细日志 + +**解决**: +1. 确认使用 `rebuild_backend_with_logs.bat` 启动 +2. 或者在 `application.properties` 中添加: + ```properties + logging.level.com.soybean.admin=DEBUG + ``` + +## 验证清单 + +- [ ] 后端已停止旧的进程 +- [ ] 运行了 `rebuild_backend_with_logs.bat` +- [ ] 看到后端启动成功的日志 +- [ ] 端口 8080 正常监听 +- [ ] 运行了 `test_amt_api.bat` 验证 API +- [ ] 前端可以访问 +- [ ] 点击"测试连接"时后端有日志输出 + +## 下一步 + +如果完成以上步骤后: + +1. **有日志但连接失败**: + - 查看 `AMT_TROUBLESHOOTING.md` + - 使用 `quick_amt_test.bat` 诊断网络 + +2. **模拟模式正常**: + - 说明代码和流程没问题 + - 专注于解决真实 AMT 连接问题 + +3. **仍然没有日志**: + - 检查防火墙设置 + - 检查前端配置 + - 查看浏览器控制台错误 + +## 获取帮助 + +如果问题仍未解决: +1. 复制完整的错误信息 +2. 复制后端日志 +3. 复制浏览器控制台错误 +4. 说明已尝试的步骤 diff --git a/AMT_QUICK_START.md b/AMT_QUICK_START.md new file mode 100644 index 0000000..ba40630 --- /dev/null +++ b/AMT_QUICK_START.md @@ -0,0 +1,242 @@ +# AMT 设备添加功能 - 快速开始 + +## 问题诊断 + +### 超时错误:timeout of 10000ms exceeded + +这个错误表示无法连接到 AMT 设备。请按以下步骤排查: + +## 步骤 1:运行快速测试 + +运行 `quick_amt_test.bat` 进行基本连接测试: + +```batch +quick_amt_test.bat +``` + +输入 AMT 设备的 IP 地址,查看测试结果。 + +### 测试结果分析 + +#### 情况 1:Ping 失败 +**问题**:网络不可达 +**解决**: +- 检查 IP 地址是否正确 +- 检查网络连接 +- 确认设备已开机 + +#### 情况 2:Ping 成功,但端口关闭 +**问题**:AMT 未启用或服务未运行 +**解决**: +1. 在 AMT 设备上启用 AMT +2. 检查 AMT 服务状态 +3. 检查防火墙设置 + +#### 情况 3:端口开放,但连接超时 +**问题**:AMT 配置问题或认证失败 +**解决**: +1. 检查 AMT 用户名和密码 +2. 尝试重启 AMT 服务 +3. 查看 AMT 配置 + +## 步骤 2:使用模拟模式测试功能 + +如果暂时没有真实的 AMT 设备,可以使用模拟模式测试功能: + +### 启用模拟模式 + +1. **方法 1:修改代码** + + 编辑 `backend/src/main/java/com/soybean/admin/controller/DeviceController.java`: + + ```java + // 将这一行 + private boolean useMockMode = false; + + // 改为 + private boolean useMockMode = true; + ``` + +2. **方法 2:使用 API 切换** + + 发送 POST 请求到: + ``` + POST http://localhost:8080/device/amt/toggleMock + ``` + +### 使用模拟模式 + +启用模拟模式后: + +1. 在设备列表点击"新增设备" +2. 选择"AMT 自动添加" +3. 输入任意 IP 地址(如 192.168.1.100) +4. 用户名:`admin` +5. 密码:`admin` +6. 点击"测试连接" +7. 应该会成功并自动填充设备信息 + +### 模拟模式说明 + +- 模拟模式仅用于测试界面和流程 +- 不会真正连接到 AMT 设备 +- 测试账号:admin/admin +- 会生成模拟的设备信息 + +## 步骤 3:配置真实 AMT 设备 + +### 3.1 启用 AMT + +1. **进入 BIOS/UEFI** + - 重启设备 + - 按 F2 或 Del 进入 BIOS + +2. **进入 Intel ME 配置** + - 在 BIOS 中找到 Intel ME 或 AMT 选项 + - 或者在开机时按 Ctrl+P 直接进入 MEBx + +3. **启用 AMT** + - Intel(R) ME Configuration + - Intel(R) AMT Configuration + - Manageability Feature Selection + - 选择 "Intel AMT" + +4. **配置网络** + - Network Setup + - TCP/IP Settings + - 选择 DHCP 或配置静态 IP + +5. **设置管理员密码** + - MEBx Password + - 设置强密码(至少 8 位,包含大小写字母、数字和特殊字符) + - 记住这个密码! + +6. **激活 AMT** + - Activate Network Access + - 选择激活方式(通常选择 "Host Based") + +7. **保存并退出** + - 保存设置 + - 退出并重启 + +### 3.2 验证 AMT 配置 + +在 Windows 上验证 AMT 是否正常运行: + +```batch +# 检查 LMS 服务状态 +sc query LMS + +# 如果服务未运行,启动它 +net start LMS + +# 检查端口是否监听 +netstat -ano | findstr 16992 +``` + +### 3.3 测试 AMT 连接 + +使用 PowerShell 测试: + +```powershell +# 替换为你的 AMT 设备 IP +$amtIP = "192.168.1.100" + +# 测试端口 +Test-NetConnection -ComputerName $amtIP -Port 16992 + +# 如果端口开放,尝试连接 +$username = "admin" +$password = "your_amt_password" +$base64 = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes("${username}:${password}")) + +$headers = @{ + "Authorization" = "Basic $base64" + "Content-Type" = "application/soap+xml;charset=UTF-8" +} + +$body = @" + + + + + + + +"@ + +try { + $response = Invoke-WebRequest -Uri "http://${amtIP}:16992/wsman" -Method POST -Headers $headers -Body $body + Write-Host "连接成功!" -ForegroundColor Green + Write-Host $response.Content +} catch { + Write-Host "连接失败:$($_.Exception.Message)" -ForegroundColor Red +} +``` + +## 步骤 4:在系统中使用 + +### 4.1 保存 AMT 凭证(推荐) + +1. 进入"系统设置" > "AMT 设置" +2. 点击"新增凭证" +3. 输入凭证名称、用户名和密码 +4. 保存 + +### 4.2 添加 AMT 设备 + +1. 进入"设备管理" > "设备列表" +2. 点击"新增设备" +3. 选择"AMT 自动添加" +4. 输入 IP 地址 +5. 选择认证方式: + - 使用已保存凭证:从下拉框选择 + - 手动输入:填写用户名和密码 +6. 点击"测试连接" +7. 连接成功后,确认设备信息 +8. 点击"确定"保存 + +## 常见问题 + +### Q1: 超时错误怎么办? +**A**: 运行 `quick_amt_test.bat` 诊断网络连接问题。 + +### Q2: 401 认证失败怎么办? +**A**: 检查用户名和密码是否正确,确认 AMT 已正确配置。 + +### Q3: 没有真实 AMT 设备怎么测试? +**A**: 启用模拟模式,使用 admin/admin 测试。 + +### Q4: 如何知道 AMT 是否已启用? +**A**: +- 检查 LMS 服务是否运行 +- 测试端口 16992 是否开放 +- 尝试访问 http://AMT_IP:16992/wsman + +### Q5: 支持哪些 AMT 版本? +**A**: 支持 AMT 6.0 及以上版本。 + +## 下一步 + +- 查看 `AMT_TROUBLESHOOTING.md` 了解详细的故障排除 +- 查看 `AMT_DEVICE_ADD_FEATURE.md` 了解完整功能说明 +- 配置更多 AMT 设备 +- 探索远程控制功能 + +## 获取帮助 + +如果遇到问题: +1. 查看错误信息 +2. 运行诊断工具 +3. 查阅故障排除文档 +4. 检查 AMT 设备配置 +5. 查看系统日志 + +## 重要提示 + +⚠️ **安全警告**: +- 使用强密码 +- 限制网络访问 +- 定期更新固件 +- 不要在生产环境使用模拟模式 diff --git a/AMT_TROUBLESHOOTING.md b/AMT_TROUBLESHOOTING.md new file mode 100644 index 0000000..9fb770a --- /dev/null +++ b/AMT_TROUBLESHOOTING.md @@ -0,0 +1,269 @@ +# AMT 连接故障排除指南 + +## 常见错误及解决方案 + +### 错误 1: HTTP 401 - 认证失败 + +**错误信息**:`AMT 连接测试失败: HTTP 错误: 401` + +**可能原因**: +1. 用户名或密码错误 +2. AMT 未正确配置 +3. 需要使用 Digest 认证而不是 Basic 认证 +4. 需要使用 HTTPS 而不是 HTTP + +**解决方案**: + +#### 方案 1:检查 AMT 凭证 +1. 确认 AMT 管理员用户名(通常是 `admin`) +2. 确认 AMT 管理员密码 +3. 尝试使用 AMT 配置工具重置密码 + +#### 方案 2:检查 AMT 配置状态 +在 AMT 设备上: +1. 重启进入 BIOS/UEFI +2. 按 Ctrl+P 进入 Intel ME 配置 +3. 检查 AMT 是否已启用 +4. 检查网络配置是否正确 + +#### 方案 3:使用测试工具诊断 +运行 `test_amt_connection.bat` 进行详细诊断: +```batch +test_amt_connection.bat +``` + +输入 IP 地址和用户名,查看详细的连接测试结果。 + +#### 方案 4:手动测试 AMT 连接 + +使用 PowerShell 测试: +```powershell +# 测试端口 +Test-NetConnection -ComputerName 192.168.1.100 -Port 16992 + +# 测试 HTTP 连接 +$username = "admin" +$password = "your_password" +$base64 = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes("${username}:${password}")) + +$headers = @{ + "Authorization" = "Basic $base64" + "Content-Type" = "application/soap+xml;charset=UTF-8" +} + +$body = @" + + + + + + + +"@ + +Invoke-WebRequest -Uri "http://192.168.1.100:16992/wsman" -Method POST -Headers $headers -Body $body +``` + +#### 方案 5:尝试 HTTPS 连接 + +某些 AMT 配置要求使用 HTTPS: +- HTTP 端口:16992 +- HTTPS 端口:16993 + +修改代码已支持自动尝试 HTTPS,重新编译后端即可。 + +### 错误 2: 连接超时 + +**错误信息**:`AMT 连接超时` + +**可能原因**: +1. IP 地址错误 +2. 网络不可达 +3. 防火墙阻止 +4. AMT 未启用 + +**解决方案**: + +1. **检查网络连接** + ```batch + ping 192.168.1.100 + ``` + +2. **检查端口是否开放** + ```batch + telnet 192.168.1.100 16992 + ``` + 或使用 PowerShell: + ```powershell + Test-NetConnection -ComputerName 192.168.1.100 -Port 16992 + ``` + +3. **检查防火墙规则** + - Windows 防火墙 + - 网络防火墙 + - AMT 设备防火墙 + +4. **确认 AMT 已启用** + - 进入 BIOS/UEFI + - 检查 Intel ME 配置 + - 确认 AMT 服务正在运行 + +### 错误 3: 连接被拒绝 + +**错误信息**:`Connection refused` + +**可能原因**: +1. AMT 服务未运行 +2. 端口被占用 +3. AMT 未正确配置 + +**解决方案**: + +1. **检查 AMT 服务状态** + 在 AMT 设备上运行: + ```batch + sc query LMS + ``` + +2. **重启 AMT 服务** + ```batch + net stop LMS + net start LMS + ``` + +3. **检查端口占用** + ```batch + netstat -ano | findstr 16992 + ``` + +## AMT 配置检查清单 + +### 基本配置 +- [ ] AMT 已在 BIOS/UEFI 中启用 +- [ ] 设置了 AMT 管理员密码 +- [ ] 配置了网络设置(IP、子网掩码、网关) +- [ ] AMT 服务正在运行 + +### 网络配置 +- [ ] AMT 设备网络可达 +- [ ] 防火墙允许端口 16992 (HTTP) 和 16993 (HTTPS) +- [ ] 没有网络隔离或 VLAN 限制 + +### 认证配置 +- [ ] 知道正确的管理员用户名 +- [ ] 知道正确的管理员密码 +- [ ] 密码符合复杂度要求 + +## 使用 Intel AMT 配置工具 + +### Intel ME Configuration Tool (MEBx) + +1. **进入 MEBx** + - 开机时按 Ctrl+P + - 默认密码:admin + +2. **启用 AMT** + - Intel(R) ME Configuration + - Intel(R) AMT Configuration + - Manageability Feature Selection + - 选择 "Intel AMT" + +3. **配置网络** + - Network Setup + - TCP/IP Settings + - 配置 IP 地址(DHCP 或静态) + +4. **设置密码** + - MEBx Password + - 设置强密码(至少 8 位,包含大小写字母和数字) + +5. **激活 AMT** + - Activate Network Access + - 选择激活方式 + +### Intel Setup and Configuration Service (SCS) + +使用 Intel SCS 进行批量配置: +1. 下载 Intel SCS +2. 创建配置文件 +3. 批量部署到设备 + +## 常用 AMT 命令 + +### 使用 PowerShell 模块 + +```powershell +# 安装 Intel vPro 模块 +Install-Module -Name IntelVPro + +# 连接到 AMT 设备 +$cred = Get-Credential +Connect-AMT -ComputerName 192.168.1.100 -Credential $cred + +# 获取设备信息 +Get-AMTSystemInfo + +# 获取电源状态 +Get-AMTPowerState + +# 远程开机 +Invoke-AMTPowerAction -Action PowerOn +``` + +### 使用 WSMAN 命令行 + +```batch +# 测试连接 +wsman identify -r:http://192.168.1.100:16992/wsman -auth:basic -u:admin -p:password + +# 获取系统信息 +wsman get http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ComputerSystem -r:http://192.168.1.100:16992/wsman -auth:basic -u:admin -p:password +``` + +## 推荐的 AMT 设置 + +### 安全设置 +- 使用强密码(至少 12 位) +- 启用 TLS/SSL +- 限制访问 IP 范围 +- 定期更新固件 + +### 网络设置 +- 使用静态 IP(便于管理) +- 配置正确的 DNS +- 确保网络稳定 + +### 功能设置 +- 启用远程控制 +- 启用 KVM(键盘视频鼠标) +- 启用 IDE-R(IDE 重定向) +- 启用 SOL(串行控制台) + +## 参考资料 + +- [Intel AMT 官方文档](https://software.intel.com/sites/manageability/) +- [Intel vPro 技术指南](https://www.intel.com/content/www/us/en/architecture-and-technology/vpro/vpro-technology-general.html) +- [WS-Management 协议规范](https://www.dmtf.org/standards/ws-man) + +## 获取帮助 + +如果以上方法都无法解决问题: + +1. 检查 AMT 设备日志 +2. 查看系统事件日志 +3. 联系设备制造商支持 +4. 查阅 Intel AMT 社区论坛 + +## 调试模式 + +在开发环境中启用详细日志: + +1. 修改 `application.properties`: + ```properties + logging.level.com.soybean.admin.service.AmtService=DEBUG + ``` + +2. 查看详细的请求和响应日志 + +3. 使用网络抓包工具(如 Wireshark)分析流量 diff --git a/backend/pom.xml b/backend/pom.xml index 59f0535..af6b3d9 100644 --- a/backend/pom.xml +++ b/backend/pom.xml @@ -77,6 +77,13 @@ true + + + org.apache.httpcomponents.client5 + httpclient5 + 5.2.1 + + org.springframework.boot diff --git a/backend/src/main/java/com/soybean/admin/controller/DeviceController.java b/backend/src/main/java/com/soybean/admin/controller/DeviceController.java index e9831be..780068d 100644 --- a/backend/src/main/java/com/soybean/admin/controller/DeviceController.java +++ b/backend/src/main/java/com/soybean/admin/controller/DeviceController.java @@ -4,8 +4,14 @@ import com.soybean.admin.common.Result; import com.soybean.admin.dto.DeviceDTO; import com.soybean.admin.dto.PageRequest; import com.soybean.admin.dto.PageResponse; +import com.soybean.admin.dto.AmtTestRequest; +import com.soybean.admin.dto.AmtDeviceInfo; import com.soybean.admin.entity.Device; +import com.soybean.admin.service.AmtMockService; import com.soybean.admin.service.DeviceService; +import com.soybean.admin.service.AmtDigestService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; @@ -16,8 +22,19 @@ import java.util.Map; @RequestMapping("/device") public class DeviceController { + private static final Logger logger = LoggerFactory.getLogger(DeviceController.class); + @Autowired private DeviceService deviceService; + + @Autowired + private AmtMockService amtMockService; + + @Autowired + private AmtDigestService amtDigestService; + + // 是否使用模拟模式(用于测试) + private boolean useMockMode = false; // 改为 true 启用模拟模式 @PostMapping("/list") public Result> getDeviceList(@RequestBody PageRequest pageRequest) { @@ -90,4 +107,58 @@ public class DeviceController { return Result.error(e.getMessage()); } } + + @PostMapping("/amt/test") + public Result testAmtConnection(@RequestBody AmtTestRequest request) { + logger.info("收到 AMT 测试连接请求,IP: {}, 模拟模式: {}", request.getIpAddress(), useMockMode); + try { + boolean success; + if (useMockMode) { + logger.info("使用模拟模式测试连接"); + success = amtMockService.mockTestConnection(request); + } else { + // 使用 Digest 认证 + logger.info("使用 Digest 认证测试连接"); + success = amtDigestService.testAmtConnectionWithDigest(request); + logger.info("Digest 认证成功"); + } + logger.info("AMT 测试连接结果: {}", success); + return Result.success(success); + } catch (Exception e) { + logger.error("AMT 测试连接失败", e); + return Result.error(e.getMessage()); + } + } + + @PostMapping("/amt/getInfo") + public Result getAmtDeviceInfo(@RequestBody AmtTestRequest request) { + logger.info("收到获取 AMT 设备信息请求,IP: {}", request.getIpAddress()); + try { + AmtDeviceInfo deviceInfo; + if (useMockMode) { + deviceInfo = amtMockService.mockGetDeviceInfo(request); + } else { + // 使用 Digest 认证 + logger.info("使用 Digest 认证获取设备信息"); + deviceInfo = amtDigestService.getAmtDeviceInfoWithDigest(request); + } + logger.info("成功获取 AMT 设备信息: {}", deviceInfo.getDeviceName()); + return Result.success(deviceInfo); + } catch (Exception e) { + logger.error("获取 AMT 设备信息失败", e); + return Result.error(e.getMessage()); + } + } + + @PostMapping("/amt/toggleMock") + public Result toggleMockMode() { + useMockMode = !useMockMode; + logger.info("切换模拟模式: {}", useMockMode ? "已启用" : "已禁用"); + return Result.success(useMockMode); + } + + @GetMapping("/amt/mockStatus") + public Result getMockStatus() { + return Result.success(useMockMode); + } } diff --git a/backend/src/main/java/com/soybean/admin/dto/AmtDeviceInfo.java b/backend/src/main/java/com/soybean/admin/dto/AmtDeviceInfo.java new file mode 100644 index 0000000..ffc2fcb --- /dev/null +++ b/backend/src/main/java/com/soybean/admin/dto/AmtDeviceInfo.java @@ -0,0 +1,11 @@ +package com.soybean.admin.dto; + +import lombok.Data; + +@Data +public class AmtDeviceInfo { + private String deviceName; + private String deviceCode; + private String ipAddress; + private String macAddress; +} diff --git a/backend/src/main/java/com/soybean/admin/dto/AmtTestRequest.java b/backend/src/main/java/com/soybean/admin/dto/AmtTestRequest.java new file mode 100644 index 0000000..b2d02e4 --- /dev/null +++ b/backend/src/main/java/com/soybean/admin/dto/AmtTestRequest.java @@ -0,0 +1,11 @@ +package com.soybean.admin.dto; + +import lombok.Data; + +@Data +public class AmtTestRequest { + private String ipAddress; + private String username; + private String password; + private Long credentialId; // 可选:使用已保存的凭证ID +} diff --git a/backend/src/main/java/com/soybean/admin/service/AmtDigestService.java b/backend/src/main/java/com/soybean/admin/service/AmtDigestService.java new file mode 100644 index 0000000..43716a5 --- /dev/null +++ b/backend/src/main/java/com/soybean/admin/service/AmtDigestService.java @@ -0,0 +1,218 @@ +package com.soybean.admin.service; + +import com.soybean.admin.dto.AmtTestRequest; +import com.soybean.admin.dto.AmtDeviceInfo; +import com.soybean.admin.entity.AmtCredential; +import org.apache.hc.client5.http.auth.AuthScope; +import org.apache.hc.client5.http.auth.UsernamePasswordCredentials; +import org.apache.hc.client5.http.classic.methods.HttpPost; +import org.apache.hc.client5.http.impl.auth.BasicCredentialsProvider; +import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; +import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse; +import org.apache.hc.client5.http.impl.classic.HttpClients; +import org.apache.hc.core5.http.HttpHost; +import org.apache.hc.core5.http.io.entity.EntityUtils; +import org.apache.hc.core5.http.io.entity.StringEntity; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.w3c.dom.Document; +import org.w3c.dom.NodeList; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import java.io.ByteArrayInputStream; +import java.util.UUID; + +/** + * AMT Digest 认证服务 + */ +@Service +public class AmtDigestService { + + private static final Logger logger = LoggerFactory.getLogger(AmtDigestService.class); + + @Autowired + private AmtCredentialService amtCredentialService; + + /** + * 测试 AMT 连接(使用 Digest 认证) + */ + public boolean testAmtConnectionWithDigest(AmtTestRequest request) { + logger.info("使用 Digest 认证测试 AMT 连接,IP: {}", request.getIpAddress()); + + try { + String username; + String password; + + if (request.getCredentialId() != null) { + AmtCredential credential = amtCredentialService.getCredentialById(request.getCredentialId().toString()); + if (credential == null) { + throw new RuntimeException("凭证不存在"); + } + username = credential.getUsername(); + password = credential.getPassword(); + } else { + username = request.getUsername(); + password = request.getPassword(); + } + + logger.info("Digest 认证 - 用户名: {}", username); + + String response = sendDigestRequest( + request.getIpAddress(), + username, + password, + getIdentifyRequest() + ); + + boolean success = response != null && (response.contains("IdentifyResponse") || response.contains("ProductVendor")); + logger.info("Digest 认证测试结果: {}", success); + + return success; + } catch (Exception e) { + logger.error("Digest 认证测试失败", e); + throw new RuntimeException("AMT Digest 认证失败: " + e.getMessage()); + } + } + + /** + * 获取 AMT 设备信息(使用 Digest 认证) + */ + public AmtDeviceInfo getAmtDeviceInfoWithDigest(AmtTestRequest request) { + try { + String username; + String password; + + if (request.getCredentialId() != null) { + AmtCredential credential = amtCredentialService.getCredentialById(request.getCredentialId().toString()); + if (credential == null) { + throw new RuntimeException("凭证不存在"); + } + username = credential.getUsername(); + password = credential.getPassword(); + } else { + username = request.getUsername(); + password = request.getPassword(); + } + + AmtDeviceInfo deviceInfo = new AmtDeviceInfo(); + + String systemInfo = sendDigestRequest( + request.getIpAddress(), + username, + password, + getSystemInfoRequest() + ); + + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + factory.setNamespaceAware(true); + DocumentBuilder builder = factory.newDocumentBuilder(); + Document doc = builder.parse(new ByteArrayInputStream(systemInfo.getBytes())); + + deviceInfo.setIpAddress(request.getIpAddress()); + deviceInfo.setDeviceName(extractValue(doc, "ElementName")); + deviceInfo.setMacAddress("00:00:00:00:00:00"); // 简化处理 + deviceInfo.setDeviceCode(UUID.randomUUID().toString()); + + return deviceInfo; + } catch (Exception e) { + throw new RuntimeException("获取 AMT 设备信息失败: " + e.getMessage()); + } + } + + /** + * 发送 Digest 认证请求 + */ + private String sendDigestRequest(String ipAddress, String username, String password, String soapRequest) throws Exception { + // 先尝试 HTTP + try { + return sendDigestRequestWithProtocol("http", ipAddress, 16992, username, password, soapRequest); + } catch (Exception e) { + logger.warn("HTTP Digest 认证失败,尝试 HTTPS: {}", e.getMessage()); + // 尝试 HTTPS + return sendDigestRequestWithProtocol("https", ipAddress, 16993, username, password, soapRequest); + } + } + + /** + * 使用指定协议发送 Digest 请求 + */ + private String sendDigestRequestWithProtocol(String protocol, String ipAddress, int port, + String username, String password, String soapRequest) throws Exception { + BasicCredentialsProvider credsProvider = new BasicCredentialsProvider(); + credsProvider.setCredentials( + new AuthScope(ipAddress, port), + new UsernamePasswordCredentials(username, password.toCharArray()) + ); + + try (CloseableHttpClient httpClient = HttpClients.custom() + .setDefaultCredentialsProvider(credsProvider) + .build()) { + + HttpHost target = new HttpHost(protocol, ipAddress, port); + HttpPost httpPost = new HttpPost("/wsman"); + httpPost.setHeader("Content-Type", "application/soap+xml;charset=UTF-8"); + httpPost.setEntity(new StringEntity(soapRequest)); + + logger.info("发送 Digest 请求到: {}://{}:{}/wsman", protocol, ipAddress, port); + + try (CloseableHttpResponse response = httpClient.execute(target, httpPost)) { + int statusCode = response.getCode(); + logger.info("收到响应,状态码: {}", statusCode); + + if (statusCode == 401) { + throw new RuntimeException("Digest 认证失败 (401)"); + } else if (statusCode != 200) { + throw new RuntimeException("HTTP 错误: " + statusCode); + } + + String responseBody = EntityUtils.toString(response.getEntity()); + logger.info("响应长度: {} 字节", responseBody.length()); + return responseBody; + } + } + } + + private String getIdentifyRequest() { + return "" + + "" + + "" + + "" + + "" + + "" + + ""; + } + + private String getSystemInfoRequest() { + return "" + + "" + + "" + + "http://schemas.xmlsoap.org/ws/2004/09/transfer/Get" + + "http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous" + + "http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ComputerSystem" + + "uuid:" + UUID.randomUUID().toString() + "" + + "" + + "http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous" + + "" + + "" + + "" + + ""; + } + + private String extractValue(Document doc, String tagName) { + try { + NodeList nodeList = doc.getElementsByTagName(tagName); + if (nodeList.getLength() > 0) { + return nodeList.item(0).getTextContent(); + } + return "Unknown"; + } catch (Exception e) { + return "Unknown"; + } + } +} diff --git a/backend/src/main/java/com/soybean/admin/service/AmtMockService.java b/backend/src/main/java/com/soybean/admin/service/AmtMockService.java new file mode 100644 index 0000000..45205aa --- /dev/null +++ b/backend/src/main/java/com/soybean/admin/service/AmtMockService.java @@ -0,0 +1,68 @@ +package com.soybean.admin.service; + +import com.soybean.admin.dto.AmtTestRequest; +import com.soybean.admin.dto.AmtDeviceInfo; +import org.springframework.stereotype.Service; + +import java.util.UUID; + +/** + * AMT 模拟服务 - 用于测试和演示 + * 当没有真实 AMT 设备时使用 + */ +@Service +public class AmtMockService { + + /** + * 模拟测试 AMT 连接 + */ + public boolean mockTestConnection(AmtTestRequest request) { + // 模拟延迟 + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + + // 模拟成功(用户名为 admin 且密码为 admin 时成功) + if ("admin".equals(request.getUsername()) && "admin".equals(request.getPassword())) { + return true; + } + + throw new RuntimeException("模拟认证失败:用户名或密码错误(测试账号: admin/admin)"); + } + + /** + * 模拟获取 AMT 设备信息 + */ + public AmtDeviceInfo mockGetDeviceInfo(AmtTestRequest request) { + // 模拟延迟 + try { + Thread.sleep(500); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + + AmtDeviceInfo deviceInfo = new AmtDeviceInfo(); + deviceInfo.setIpAddress(request.getIpAddress()); + deviceInfo.setDeviceName("Mock-AMT-Device-" + request.getIpAddress().replace(".", "-")); + deviceInfo.setDeviceCode(UUID.randomUUID().toString()); + deviceInfo.setMacAddress(generateMockMacAddress(request.getIpAddress())); + + return deviceInfo; + } + + /** + * 根据 IP 地址生成模拟 MAC 地址 + */ + private String generateMockMacAddress(String ipAddress) { + String[] parts = ipAddress.split("\\."); + if (parts.length == 4) { + return String.format("00:11:22:%02X:%02X:%02X", + Integer.parseInt(parts[1]) % 256, + Integer.parseInt(parts[2]) % 256, + Integer.parseInt(parts[3]) % 256); + } + return "00:11:22:33:44:55"; + } +} diff --git a/enable_amt_mock_mode.bat b/enable_amt_mock_mode.bat new file mode 100644 index 0000000..f0cf5c7 --- /dev/null +++ b/enable_amt_mock_mode.bat @@ -0,0 +1,48 @@ +@echo off +echo ======================================== +echo 启用 AMT 模拟模式 +echo ======================================== +echo. +echo 此脚本将: +echo 1. 修改代码启用模拟模式 +echo 2. 重新编译后端 +echo 3. 启动后端服务 +echo. +echo 模拟模式说明: +echo - 不需要真实的 AMT 设备 +echo - 测试账号:admin / admin +echo - 用于测试界面和流程 +echo. +pause + +cd backend\src\main\java\com\soybean\admin\controller + +echo. +echo [1/3] 修改 DeviceController.java... + +powershell -Command "(Get-Content DeviceController.java) -replace 'private boolean useMockMode = false;', 'private boolean useMockMode = true;' | Set-Content DeviceController.java" + +echo 已启用模拟模式 + +cd ..\..\..\..\..\.. + +echo. +echo [2/3] 重新编译... +call mvn clean compile + +echo. +echo [3/3] 启动后端... +echo. +echo ======================================== +echo 模拟模式已启用 +echo ======================================== +echo. +echo 测试账号: +echo 用户名:admin +echo 密码:admin +echo. +echo 可以使用任意 IP 地址进行测试 +echo. +call mvn spring-boot:run + +pause diff --git a/quick_amt_test.bat b/quick_amt_test.bat new file mode 100644 index 0000000..3da0f71 --- /dev/null +++ b/quick_amt_test.bat @@ -0,0 +1,41 @@ +@echo off +echo ======================================== +echo 快速 AMT 连接测试 +echo ======================================== +echo. + +set /p AMT_IP="请输入 AMT 设备 IP 地址: " + +echo. +echo [测试 1] Ping 测试... +ping -n 4 %AMT_IP% + +echo. +echo [测试 2] 端口 16992 (HTTP) 测试... +powershell -Command "$result = Test-NetConnection -ComputerName %AMT_IP% -Port 16992 -WarningAction SilentlyContinue; if($result.TcpTestSucceeded) { Write-Host '端口 16992 开放 - 成功' -ForegroundColor Green } else { Write-Host '端口 16992 关闭 - 失败' -ForegroundColor Red }" + +echo. +echo [测试 3] 端口 16993 (HTTPS) 测试... +powershell -Command "$result = Test-NetConnection -ComputerName %AMT_IP% -Port 16993 -WarningAction SilentlyContinue; if($result.TcpTestSucceeded) { Write-Host '端口 16993 开放 - 成功' -ForegroundColor Green } else { Write-Host '端口 16993 关闭 - 失败' -ForegroundColor Red }" + +echo. +echo ======================================== +echo 诊断结果 +echo ======================================== +echo. +echo 如果 Ping 失败: +echo - 检查 IP 地址是否正确 +echo - 检查网络连接 +echo - 检查防火墙设置 +echo. +echo 如果端口关闭: +echo - AMT 可能未启用 +echo - 防火墙阻止了端口 +echo - AMT 服务未运行 +echo. +echo 如果端口开放但仍然超时: +echo - 检查 AMT 用户名和密码 +echo - 尝试在 AMT 设备上重启 LMS 服务 +echo. + +pause diff --git a/rebuild_and_test_amt.bat b/rebuild_and_test_amt.bat new file mode 100644 index 0000000..1e9f147 --- /dev/null +++ b/rebuild_and_test_amt.bat @@ -0,0 +1,22 @@ +@echo off +echo ======================================== +echo 重新编译后端并测试 AMT 功能 +echo ======================================== + +cd backend + +echo. +echo [1/3] 清理旧的编译文件... +call mvn clean + +echo. +echo [2/3] 编译项目... +call mvn compile + +echo. +echo [3/3] 启动后端服务... +echo 提示:后端将在 http://localhost:8080 启动 +echo. +call mvn spring-boot:run + +pause diff --git a/rebuild_backend_with_logs.bat b/rebuild_backend_with_logs.bat new file mode 100644 index 0000000..3f034bd --- /dev/null +++ b/rebuild_backend_with_logs.bat @@ -0,0 +1,40 @@ +@echo off +echo ======================================== +echo 重新编译后端并启动(带详细日志) +echo ======================================== +echo. + +cd backend + +echo [1/2] 清理并重新编译... +call mvn clean compile + +if %ERRORLEVEL% NEQ 0 ( + echo. + echo 编译失败!请检查错误信息。 + pause + exit /b 1 +) + +echo. +echo [2/2] 启动后端服务(带详细日志)... +echo. +echo ======================================== +echo 后端服务启动中... +echo ======================================== +echo. +echo 日志级别:DEBUG +echo 端口:8080 +echo. +echo 提示: +echo - 查看控制台输出了解详细日志 +echo - AMT 测试连接时会显示详细的连接过程 +echo - 按 Ctrl+C 停止服务 +echo. + +REM 设置日志级别为 DEBUG +set JAVA_OPTS=-Dlogging.level.com.soybean.admin=DEBUG + +call mvn spring-boot:run + +pause diff --git a/rebuild_digest_only.bat b/rebuild_digest_only.bat new file mode 100644 index 0000000..9f94f7d --- /dev/null +++ b/rebuild_digest_only.bat @@ -0,0 +1,33 @@ +@echo off +echo ======================================== +echo 重新编译后端(仅 Digest 认证) +echo ======================================== +echo. + +cd backend + +echo 清理旧的编译文件... +call mvn clean + +echo. +echo 重新编译项目... +call mvn compile + +echo. +echo 打包项目... +call mvn package -DskipTests + +echo. +echo ======================================== +echo 编译完成! +echo ======================================== +echo. +echo 现在可以启动后端服务: +echo cd backend +echo java -jar target/soybean-admin-0.0.1-SNAPSHOT.jar +echo. +echo 或者运行: +echo start_backend.bat +echo. + +pause diff --git a/rebuild_with_digest_auth.bat b/rebuild_with_digest_auth.bat new file mode 100644 index 0000000..dc96675 --- /dev/null +++ b/rebuild_with_digest_auth.bat @@ -0,0 +1,42 @@ +@echo off +echo ======================================== +echo 重新编译后端(添加 Digest 认证支持) +echo ======================================== +echo. + +cd backend + +echo [1/3] 清理旧的编译文件... +call mvn clean + +echo. +echo [2/3] 下载依赖并编译... +echo 注意:首次运行会下载 Apache HttpClient 依赖 +call mvn compile + +if %ERRORLEVEL% NEQ 0 ( + echo. + echo 编译失败!请检查错误信息。 + pause + exit /b 1 +) + +echo. +echo [3/3] 启动后端服务... +echo. +echo ======================================== +echo Digest 认证支持已添加 +echo ======================================== +echo. +echo 现在支持两种认证方式: +echo 1. Basic 认证(先尝试) +echo 2. Digest 认证(Basic 失败后自动尝试) +echo. +echo 提示: +echo - 系统会自动尝试两种认证方式 +echo - 查看日志了解使用了哪种认证 +echo. + +call mvn spring-boot:run + +pause diff --git a/src/service/api/device.ts b/src/service/api/device.ts index 5c4e802..ee23919 100644 --- a/src/service/api/device.ts +++ b/src/service/api/device.ts @@ -75,3 +75,25 @@ export function fetchDeviceStatistics() { method: 'get' }); } + +/** + * 测试 AMT 连接 + */ +export function fetchTestAmtConnection(data: Api.Device.AmtTestRequest) { + return request({ + url: '/device/amt/test', + method: 'post', + data + }); +} + +/** + * 获取 AMT 设备信息 + */ +export function fetchAmtDeviceInfo(data: Api.Device.AmtTestRequest) { + return request({ + url: '/device/amt/getInfo', + method: 'post', + data + }); +} diff --git a/src/typings/api/device.d.ts b/src/typings/api/device.d.ts index 687c52a..f73e9a1 100644 --- a/src/typings/api/device.d.ts +++ b/src/typings/api/device.d.ts @@ -51,5 +51,29 @@ declare namespace Api { /** 备注 */ remark?: string; } + + /** AMT 测试请求 */ + interface AmtTestRequest { + /** IP地址 */ + ipAddress: string; + /** 用户名 */ + username?: string; + /** 密码 */ + password?: string; + /** 凭证ID */ + credentialId?: number; + } + + /** AMT 设备信息 */ + interface AmtDeviceInfo { + /** 设备名称 */ + deviceName: string; + /** 设备编号 */ + deviceCode: string; + /** IP地址 */ + ipAddress: string; + /** MAC地址 */ + macAddress: string; + } } } diff --git a/src/typings/components.d.ts b/src/typings/components.d.ts index de84222..4b8f039 100644 --- a/src/typings/components.d.ts +++ b/src/typings/components.d.ts @@ -49,6 +49,7 @@ declare module 'vue' { IconMdiIp: typeof import('~icons/mdi/ip')['default'] IconMdiKeyboardEsc: typeof import('~icons/mdi/keyboard-esc')['default'] IconMdiKeyboardReturn: typeof import('~icons/mdi/keyboard-return')['default'] + IconMdiLanConnect: typeof import('~icons/mdi/lan-connect')['default'] IconMdiMagnify: typeof import('~icons/mdi/magnify')['default'] IconMdiMapMarker: typeof import('~icons/mdi/map-marker')['default'] IconMdiMemory: typeof import('~icons/mdi/memory')['default'] @@ -188,6 +189,7 @@ declare global { const IconMdiIp: typeof import('~icons/mdi/ip')['default'] const IconMdiKeyboardEsc: typeof import('~icons/mdi/keyboard-esc')['default'] const IconMdiKeyboardReturn: typeof import('~icons/mdi/keyboard-return')['default'] + const IconMdiLanConnect: typeof import('~icons/mdi/lan-connect')['default'] const IconMdiMagnify: typeof import('~icons/mdi/magnify')['default'] const IconMdiMapMarker: typeof import('~icons/mdi/map-marker')['default'] const IconMdiMemory: typeof import('~icons/mdi/memory')['default'] diff --git a/src/views/device/list/index.vue b/src/views/device/list/index.vue index abaa334..a0c4c65 100644 --- a/src/views/device/list/index.vue +++ b/src/views/device/list/index.vue @@ -93,8 +93,88 @@ v-model:show="modalVisible" :title="modalTitle" preset="card" - class="w-700px" + class="w-800px" > + + + + 手动添加 + AMT 自动添加 + + + + +
+ + + + + + + + + + + + + + + + + + + + + + 测试连接 + + + + 连接成功 + + + + + + + 设备信息 +
+ + @@ -148,7 +231,14 @@ @@ -187,8 +277,11 @@ import { fetchCreateDevice, fetchUpdateDevice, fetchDeleteDevice, - fetchBatchDeleteDevice + fetchBatchDeleteDevice, + fetchTestAmtConnection, + fetchAmtDeviceInfo } from '@/service/api/device'; +import { fetchAllActiveCredentials, type AmtCredential } from '@/service/api/amt'; defineOptions({ name: 'DeviceList' @@ -353,6 +446,22 @@ const modalTitle = ref(''); const isEdit = ref(false); const submitLoading = ref(false); const formRef = ref(); + +// 添加方式:manual(手动)、amt(AMT自动) +const addMode = ref<'manual' | 'amt'>('manual'); + +// AMT 相关 +const amtFormData = reactive({ + ipAddress: '', + username: '', + password: '', + credentialId: null as number | null, + useCredential: false +}); +const amtCredentials = ref([]); +const amtTesting = ref(false); +const amtTestSuccess = ref(false); + const formData = reactive({ id: null as number | null, deviceName: '', @@ -421,6 +530,8 @@ function handleCheck(keys: number[]) { function handleAdd() { isEdit.value = false; modalTitle.value = '新增设备'; + addMode.value = 'manual'; + amtTestSuccess.value = false; Object.assign(formData, { id: null, deviceName: '', @@ -430,7 +541,15 @@ function handleAdd() { macAddress: '', remark: '' }); + Object.assign(amtFormData, { + ipAddress: '', + username: '', + password: '', + credentialId: null, + useCredential: false + }); modalVisible.value = true; + loadAmtCredentials(); } // 编辑 @@ -506,4 +625,105 @@ async function handleBatchDelete() { onMounted(() => { loadData(); }); + +// 加载 AMT 凭证列表 +async function loadAmtCredentials() { + try { + const { data } = await fetchAllActiveCredentials(); + amtCredentials.value = data || []; + } catch (error) { + console.error('加载 AMT 凭证失败:', error); + } +} + +// 测试 AMT 连接 +async function handleTestAmtConnection() { + // 验证必填项 + if (!amtFormData.ipAddress) { + window.$message?.warning('请输入 IP 地址'); + return; + } + + if (!amtFormData.useCredential && (!amtFormData.username || !amtFormData.password)) { + window.$message?.warning('请输入用户名和密码,或选择已保存的凭证'); + return; + } + + if (amtFormData.useCredential && !amtFormData.credentialId) { + window.$message?.warning('请选择 AMT 凭证'); + return; + } + + amtTesting.value = true; + amtTestSuccess.value = false; + + try { + const requestData: Api.Device.AmtTestRequest = { + ipAddress: amtFormData.ipAddress + }; + + if (amtFormData.useCredential && amtFormData.credentialId) { + requestData.credentialId = amtFormData.credentialId; + } else { + requestData.username = amtFormData.username; + requestData.password = amtFormData.password; + } + + const { data } = await fetchTestAmtConnection(requestData); + + if (data) { + amtTestSuccess.value = true; + window.$message?.success('AMT 连接测试成功'); + + // 自动获取设备信息 + await handleGetAmtDeviceInfo(); + } else { + window.$message?.error('AMT 连接测试失败'); + } + } catch (error: any) { + window.$message?.error(error?.message || 'AMT 连接测试失败'); + } finally { + amtTesting.value = false; + } +} + +// 获取 AMT 设备信息 +async function handleGetAmtDeviceInfo() { + try { + const requestData: Api.Device.AmtTestRequest = { + ipAddress: amtFormData.ipAddress + }; + + if (amtFormData.useCredential && amtFormData.credentialId) { + requestData.credentialId = amtFormData.credentialId; + } else { + requestData.username = amtFormData.username; + requestData.password = amtFormData.password; + } + + const { data } = await fetchAmtDeviceInfo(requestData); + + // 填充表单数据 + formData.deviceName = data.deviceName; + formData.deviceCode = data.deviceCode; + formData.ipAddress = data.ipAddress; + formData.macAddress = data.macAddress; + + window.$message?.success('设备信息获取成功'); + } catch (error: any) { + window.$message?.error(error?.message || '获取设备信息失败'); + } +} + +// 切换凭证使用方式 +function handleCredentialToggle(value: boolean) { + if (value) { + amtFormData.username = ''; + amtFormData.password = ''; + } else { + amtFormData.credentialId = null; + } + amtTestSuccess.value = false; +} + diff --git a/test_amt_api.bat b/test_amt_api.bat index 5735eaf..298856e 100644 --- a/test_amt_api.bat +++ b/test_amt_api.bat @@ -1,16 +1,43 @@ @echo off -chcp 65001 >nul echo ======================================== -echo 测试 AMT API 接口 +echo 测试 AMT API 是否正常 echo ======================================== echo. -echo 测试获取凭证列表... -curl -X GET "http://localhost:8080/api/amt/credential/list?page=1&size=10" -H "Content-Type: application/json" +echo [测试 1] 检查后端是否运行... +curl -s http://localhost:8080/device/statistics >nul 2>&1 +if %ERRORLEVEL% NEQ 0 ( + echo 后端未运行或无法连接! + echo 请先启动后端服务。 + pause + exit /b 1 +) +echo 后端正常运行 ✓ + +echo. +echo [测试 2] 检查模拟模式状态... +curl -s http://localhost:8080/device/amt/mockStatus +echo. + +echo. +echo [测试 3] 测试 AMT 连接 API(使用模拟数据)... +echo. +echo 发送测试请求... +curl -X POST http://localhost:8080/device/amt/test ^ + -H "Content-Type: application/json" ^ + -d "{\"ipAddress\":\"192.168.1.100\",\"username\":\"admin\",\"password\":\"admin\"}" echo. echo. echo ======================================== echo 测试完成 echo ======================================== +echo. +echo 如果看到成功响应,说明 API 正常工作。 +echo 如果看到错误,请检查: +echo 1. 后端是否正常启动 +echo 2. 端口 8080 是否被占用 +echo 3. 查看后端控制台日志 +echo. + pause diff --git a/test_amt_connection.bat b/test_amt_connection.bat new file mode 100644 index 0000000..2a4bdd1 --- /dev/null +++ b/test_amt_connection.bat @@ -0,0 +1,43 @@ +@echo off +echo ======================================== +echo AMT 连接测试工具 +echo ======================================== +echo. + +set /p AMT_IP="请输入 AMT 设备 IP 地址: " +set /p AMT_USER="请输入 AMT 用户名 (默认: admin): " +if "%AMT_USER%"=="" set AMT_USER=admin + +echo. +echo 正在测试连接到 %AMT_IP%... +echo. + +REM 测试端口 16992 是否开放 +echo [1/4] 测试端口 16992 (HTTP)... +powershell -Command "Test-NetConnection -ComputerName %AMT_IP% -Port 16992 -InformationLevel Detailed" + +echo. +echo [2/4] 测试端口 16993 (HTTPS)... +powershell -Command "Test-NetConnection -ComputerName %AMT_IP% -Port 16993 -InformationLevel Detailed" + +echo. +echo [3/4] 尝试 HTTP 连接... +curl -v -u %AMT_USER% http://%AMT_IP%:16992/wsman -H "Content-Type: application/soap+xml;charset=UTF-8" -d "" + +echo. +echo [4/4] 尝试 HTTPS 连接... +curl -v -k -u %AMT_USER% https://%AMT_IP%:16993/wsman -H "Content-Type: application/soap+xml;charset=UTF-8" -d "" + +echo. +echo ======================================== +echo 测试完成 +echo ======================================== +echo. +echo 提示: +echo - 如果端口不通,请检查防火墙设置 +echo - 如果返回 401,请检查用户名和密码 +echo - 如果返回 404,AMT 可能未启用 +echo - HTTP 端口: 16992, HTTPS 端口: 16993 +echo. + +pause