Parcourir la source

驾驶舱只读接口与推送免鉴权

Kazerin il y a 2 semaines
Parent
commit
8c0771ad82
50 fichiers modifiés avec 91 ajouts et 189 suppressions
  1. 7 8
      docs/cockpit/README.md
  2. 0 2
      pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/cockpit/CockpitCurrentEventsController.java
  3. 0 2
      pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/cockpit/CockpitDeviceBoundaryController.java
  4. 0 2
      pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/cockpit/CockpitEquipmentArchiveController.java
  5. 0 2
      pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/cockpit/CockpitEquipmentDetailAggregateController.java
  6. 0 3
      pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/cockpit/CockpitEventReadController.java
  7. 0 2
      pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/cockpit/CockpitFoundationController.java
  8. 0 2
      pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/cockpit/CockpitFreshnessController.java
  9. 0 2
      pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/cockpit/CockpitHandlingEfficiencyController.java
  10. 0 2
      pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/cockpit/CockpitHistoryAnalysisController.java
  11. 0 2
      pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/cockpit/CockpitInfrastructureBaseArchivesController.java
  12. 0 2
      pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/cockpit/CockpitInfrastructureEmergencySupportController.java
  13. 0 2
      pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/cockpit/CockpitInfrastructureManagementInformationController.java
  14. 0 2
      pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/cockpit/CockpitInfrastructureOperatingInformationController.java
  15. 0 2
      pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/cockpit/CockpitMapSnapshotController.java
  16. 0 2
      pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/cockpit/CockpitMonitoringDistributionController.java
  17. 0 2
      pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/cockpit/CockpitMonitoringOverviewController.java
  18. 0 2
      pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/cockpit/CockpitMonitoringPointDetailController.java
  19. 0 2
      pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/cockpit/CockpitMonitoringSituationController.java
  20. 0 2
      pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/cockpit/CockpitMultidimensionalAnalysisController.java
  21. 0 2
      pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/cockpit/CockpitOverviewInfrastructureController.java
  22. 0 2
      pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/cockpit/CockpitOverviewMonitoringController.java
  23. 0 2
      pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/cockpit/CockpitOverviewWarningSettingsController.java
  24. 0 3
      pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/cockpit/CockpitTypeMetricsController.java
  25. 0 4
      pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/cockpit/CockpitWorkOrderReadController.java
  26. 9 4
      pipe-network-service/zksy-admin/src/main/java/com/zksy/web/websocket/DeviceStatusWebSocketHandler.java
  27. 54 0
      pipe-network-service/zksy-admin/src/test/java/com/zksy/web/controller/cockpit/CockpitAnonymousAccessSecurityConfigTest.java
  28. 1 1
      pipe-network-service/zksy-admin/src/test/java/com/zksy/web/controller/cockpit/CockpitAuthenticationEntryPointTest.java
  29. 0 6
      pipe-network-service/zksy-admin/src/test/java/com/zksy/web/controller/cockpit/CockpitCurrentEventsControllerTest.java
  30. 0 6
      pipe-network-service/zksy-admin/src/test/java/com/zksy/web/controller/cockpit/CockpitDeviceBoundaryControllerTest.java
  31. 0 7
      pipe-network-service/zksy-admin/src/test/java/com/zksy/web/controller/cockpit/CockpitEquipmentArchiveControllerTest.java
  32. 0 4
      pipe-network-service/zksy-admin/src/test/java/com/zksy/web/controller/cockpit/CockpitFoundationControllerTest.java
  33. 0 4
      pipe-network-service/zksy-admin/src/test/java/com/zksy/web/controller/cockpit/CockpitFreshnessSemanticsControllerTest.java
  34. 0 6
      pipe-network-service/zksy-admin/src/test/java/com/zksy/web/controller/cockpit/CockpitHandlingEfficiencyControllerTest.java
  35. 0 6
      pipe-network-service/zksy-admin/src/test/java/com/zksy/web/controller/cockpit/CockpitHistoryAnalysisControllerTest.java
  36. 0 7
      pipe-network-service/zksy-admin/src/test/java/com/zksy/web/controller/cockpit/CockpitInfrastructureBaseArchivesControllerTest.java
  37. 0 7
      pipe-network-service/zksy-admin/src/test/java/com/zksy/web/controller/cockpit/CockpitInfrastructureEmergencySupportControllerTest.java
  38. 0 7
      pipe-network-service/zksy-admin/src/test/java/com/zksy/web/controller/cockpit/CockpitInfrastructureManagementInformationControllerTest.java
  39. 0 7
      pipe-network-service/zksy-admin/src/test/java/com/zksy/web/controller/cockpit/CockpitInfrastructureOperatingInformationControllerTest.java
  40. 0 6
      pipe-network-service/zksy-admin/src/test/java/com/zksy/web/controller/cockpit/CockpitMapSnapshotControllerTest.java
  41. 0 6
      pipe-network-service/zksy-admin/src/test/java/com/zksy/web/controller/cockpit/CockpitMonitoringDistributionControllerTest.java
  42. 0 6
      pipe-network-service/zksy-admin/src/test/java/com/zksy/web/controller/cockpit/CockpitMonitoringOverviewControllerTest.java
  43. 0 6
      pipe-network-service/zksy-admin/src/test/java/com/zksy/web/controller/cockpit/CockpitMonitoringPointDetailControllerTest.java
  44. 0 6
      pipe-network-service/zksy-admin/src/test/java/com/zksy/web/controller/cockpit/CockpitMonitoringSituationControllerTest.java
  45. 0 6
      pipe-network-service/zksy-admin/src/test/java/com/zksy/web/controller/cockpit/CockpitMultidimensionalAnalysisControllerTest.java
  46. 0 7
      pipe-network-service/zksy-admin/src/test/java/com/zksy/web/controller/cockpit/CockpitOverviewInfrastructureControllerTest.java
  47. 0 6
      pipe-network-service/zksy-admin/src/test/java/com/zksy/web/controller/cockpit/CockpitOverviewMonitoringControllerTest.java
  48. 0 6
      pipe-network-service/zksy-admin/src/test/java/com/zksy/web/controller/cockpit/CockpitOverviewWarningSettingsControllerTest.java
  49. 17 0
      pipe-network-service/zksy-admin/src/test/java/com/zksy/web/websocket/DeviceStatusCockpitWebSocketHandlerTest.java
  50. 3 2
      pipe-network-service/zksy-framework/src/main/java/com/zksy/framework/config/SecurityConfig.java

+ 7 - 8
docs/cockpit/README.md

@@ -4,7 +4,7 @@
 
 ## 1. 契约总则
 
-- 所有接口均为**只读**:仅 `GET` + `@PreAuthorize("isAuthenticated()")`,不存在任何处置、派发、确认类写入口。
+- 所有接口均为**只读**:仅 `GET`,不存在任何处置、派发、确认类写入口。**2026-09-28 起驾驶舱 GET 接口免鉴权**:`GET /api/cockpit/v1/**` 匿名可访问,不再要求 `Authorization`;接口安全性由部署环境的网络层内网隔离保障。非 GET 请求、其它业务 API 与管理端接口仍需认证。
 - 统一响应信封:`{ "code": 200, "msg": "操作成功", "data": {...} }`;错误为 `{ "code": <httpStatus>, "msg": "<message>", "data": { category, message, requestId, serverTime } }`。
 - JSON 字段一律 `camelCase`。数据库列名属适配器内部细节,不出现在契约中。
 - 设备唯一关联键:对外使用 `deviceCode`(对应 `equipment_base.equipment_code`)。设备主键内部使用 `equipment_base.equipment_id`;`work_order` 当前通过 `device_id` 关联设备,不能读取已删除的 `work_order.device_code`。WebSocket 迁移期同时下发 `device_code` 作为兼容别名。
@@ -107,10 +107,10 @@
 
 ## 5. WebSocket 实时增量
 
-端点:`/ws/deviceStatus`。
+端点:`/ws/deviceStatus`(**免鉴权**,与驾驶舱 REST 一致;安全性由网络层内网隔离保障)。
 
 ```
-// 订阅全量驾驶舱状态
+// 订阅全量驾驶舱状态:连接后直接发送,无需 authenticate 帧(2026-09-28 起)
 → {"type":"cockpitSubscribe","scope":"all"}
 ← {"type":"cockpitSubscribed","scope":"all","serverUpdatedAt":"2026-09-02T10:00:00+08:00"}
 
@@ -121,12 +121,10 @@
    "monitoringPointCode":"MP-001",
    "data":{"status":"ALARM","statusTime":"2026-09-02T09:59:50+08:00",
            "serverUpdatedAt":"2026-09-02T10:00:00+08:00"}}
-
-// 兼容旧客户端:按设备订阅
-→ {"type":"subscribe","deviceCodes":["TC-PS-WS-202609020012"]}
-← {"type":"subscribed","deviceCodes":["TC-PS-WS-202609020012"]}
 ```
 
+`authenticate` 帧仅服务管理端工单推送(`workOrderReminder` 需要用户身份),驾驶舱链路不使用、不要求。
+
 客户端以 `serverUpdatedAt` 判新旧(新者胜),REST 提供首帧权威快照,WebSocket 只推变化设备;断线后重新订阅即可。设备状态校准周期默认 60 秒,可配置延长。
 
 增量帧字段口径:`monitoringPointCode` 为设备关联监测点的编码(`equipment_base.equipment_code` → `equipment_point_rel` → `monitor_point.point_code`);未关联设备该字段显式 `null`,不静默丢失。`data.reason` 无异常时显式 `null`。
@@ -151,13 +149,14 @@ mvn -pl zksy-admin -am test -Dtest='Cockpit*Test'
 
 联调检查表:
 
-1. 携带正确凭据访问任一端点,返回 200 且 `data.readOnly = true`;无凭据返回 401 `UNAUTHORIZED`。
+1. **无凭据** `GET` 任一驾驶舱端点,返回 200 且 `data.readOnly = true`(2026-09-28 起免鉴权;接口安全由网络层内网隔离保障)。
 2. 对每个端点构造一次非法参数,确认 400 `INVALID_REQUEST` 且不产生业务写入。
 3. 用 `snapshotAt` 连续请求 `page=1..n`,确认 `total` 一致、监测点不重复。
 4. 用同一 `deviceCode` 依次请求 archive / metrics / events / work-orders / detail-aggregate,确认关联一致。
 5. `detail-aggregate` 中任一分区失败时,其余分区仍返回数据。
 6. 建立 WebSocket,发送 `cockpitSubscribe(scope=all)`,确认收到 `cockpitSubscribed` 与后续增量。
 7. 校验所有时间戳均为 `+08:00`,`freshness` 与实际数据新鲜度一致。
+8. 对任一驾驶舱端点发非 GET 请求(如 POST),确认仍被拒绝(405 由映射决定,其余非 GET 仍需认证)。
 
 ## 7. 国产化与容器化可行性
 

+ 0 - 2
pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/cockpit/CockpitCurrentEventsController.java

@@ -9,7 +9,6 @@ import com.zksy.common.utils.StringUtils;
 import com.zksy.common.utils.uuid.IdUtils;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
-import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.ExceptionHandler;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.RequestHeader;
@@ -29,7 +28,6 @@ public class CockpitCurrentEventsController {
     }
 
     @GetMapping("/api/cockpit/v1/events/current")
-    @PreAuthorize("isAuthenticated()")
     public ResponseEntity<AjaxResult> currentEvents(
             @RequestParam(value = "mode", required = false) String mode,
             @RequestParam(value = "level", required = false) String level,

+ 0 - 2
pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/cockpit/CockpitDeviceBoundaryController.java

@@ -8,7 +8,6 @@ import com.zksy.common.utils.StringUtils;
 import com.zksy.common.utils.uuid.IdUtils;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
-import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.ExceptionHandler;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.RequestHeader;
@@ -27,7 +26,6 @@ public class CockpitDeviceBoundaryController {
     }
 
     @GetMapping("/api/cockpit/v1/device-boundary")
-    @PreAuthorize("isAuthenticated()")
     public ResponseEntity<AjaxResult> deviceBoundary(
             @RequestHeader(value = REQUEST_ID_HEADER, required = false) String requestId) {
         return ResponseEntity.ok(AjaxResult.success(deviceBoundaryService.load(resolveRequestId(requestId))));

+ 0 - 2
pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/cockpit/CockpitEquipmentArchiveController.java

@@ -12,7 +12,6 @@ import com.zksy.common.utils.StringUtils;
 import com.zksy.common.utils.uuid.IdUtils;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
-import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.ExceptionHandler;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.PathVariable;
@@ -35,7 +34,6 @@ public class CockpitEquipmentArchiveController {
     }
 
     @GetMapping("/api/cockpit/v1/equipments/{deviceCode}/archive")
-    @PreAuthorize("isAuthenticated()")
     public ResponseEntity<AjaxResult> equipmentArchive(
             @PathVariable("deviceCode") String deviceCode,
             @RequestHeader(value = REQUEST_ID_HEADER, required = false) String requestId) {

+ 0 - 2
pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/cockpit/CockpitEquipmentDetailAggregateController.java

@@ -10,7 +10,6 @@ import com.zksy.common.utils.StringUtils;
 import com.zksy.common.utils.uuid.IdUtils;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
-import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.ExceptionHandler;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.PathVariable;
@@ -32,7 +31,6 @@ public class CockpitEquipmentDetailAggregateController {
     }
 
     @GetMapping("/api/cockpit/v1/equipments/{deviceCode}/detail-aggregate")
-    @PreAuthorize("isAuthenticated()")
     public ResponseEntity<AjaxResult> equipmentDetailAggregate(
             @PathVariable("deviceCode") String deviceCode,
             @RequestHeader(value = REQUEST_ID_HEADER, required = false) String requestId) {

+ 0 - 3
pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/cockpit/CockpitEventReadController.java

@@ -11,7 +11,6 @@ import com.zksy.common.utils.StringUtils;
 import com.zksy.common.utils.uuid.IdUtils;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
-import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.ExceptionHandler;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.PathVariable;
@@ -34,7 +33,6 @@ public class CockpitEventReadController {
     }
 
     @GetMapping("/api/cockpit/v1/equipments/{deviceCode}/events/current")
-    @PreAuthorize("isAuthenticated()")
     public ResponseEntity<AjaxResult> currentEvents(
             @PathVariable("deviceCode") String deviceCode,
             @RequestHeader(value = REQUEST_ID_HEADER, required = false) String requestId) {
@@ -44,7 +42,6 @@ public class CockpitEventReadController {
     }
 
     @GetMapping("/api/cockpit/v1/equipments/{deviceCode}/events/history")
-    @PreAuthorize("isAuthenticated()")
     public ResponseEntity<AjaxResult> historyEvents(
             @PathVariable("deviceCode") String deviceCode,
             @RequestParam(value = "startTime") String startTime,

+ 0 - 2
pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/cockpit/CockpitFoundationController.java

@@ -8,7 +8,6 @@ import com.zksy.common.utils.StringUtils;
 import com.zksy.common.utils.uuid.IdUtils;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
-import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.ExceptionHandler;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.RequestHeader;
@@ -29,7 +28,6 @@ public class CockpitFoundationController {
     }
 
     @GetMapping("/foundation")
-    @PreAuthorize("isAuthenticated()")
     public ResponseEntity<AjaxResult> foundation(@RequestHeader(value = REQUEST_ID_HEADER, required = false) String requestId) {
         return ResponseEntity.ok(AjaxResult.success(foundationService.load(resolveRequestId(requestId))));
     }

+ 0 - 2
pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/cockpit/CockpitFreshnessController.java

@@ -13,7 +13,6 @@ import com.zksy.common.utils.uuid.IdUtils;
 import java.time.Clock;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
-import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.ExceptionHandler;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.PathVariable;
@@ -43,7 +42,6 @@ public class CockpitFreshnessController {
     }
 
     @GetMapping("/api/cockpit/v1/equipments/{deviceCode}/freshness")
-    @PreAuthorize("isAuthenticated()")
     public ResponseEntity<AjaxResult> freshness(
             @PathVariable("deviceCode") String deviceCode,
             @RequestParam(value = "calibrationPeriodSeconds", required = false) Integer calibrationPeriodSeconds,

+ 0 - 2
pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/cockpit/CockpitHandlingEfficiencyController.java

@@ -9,7 +9,6 @@ import com.zksy.common.utils.StringUtils;
 import com.zksy.common.utils.uuid.IdUtils;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
-import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.ExceptionHandler;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.RequestHeader;
@@ -30,7 +29,6 @@ public class CockpitHandlingEfficiencyController {
     }
 
     @GetMapping("/api/cockpit/v1/events/handling-efficiency")
-    @PreAuthorize("isAuthenticated()")
     public ResponseEntity<AjaxResult> handlingEfficiency(
             @RequestParam(value = "eventFamily", required = false) String eventFamily,
             @RequestParam(value = "industry", required = false) String industry,

+ 0 - 2
pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/cockpit/CockpitHistoryAnalysisController.java

@@ -9,7 +9,6 @@ import com.zksy.common.utils.StringUtils;
 import com.zksy.common.utils.uuid.IdUtils;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
-import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.ExceptionHandler;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.RequestHeader;
@@ -29,7 +28,6 @@ public class CockpitHistoryAnalysisController {
     }
 
     @GetMapping("/api/cockpit/v1/events/history-analysis")
-    @PreAuthorize("isAuthenticated()")
     public ResponseEntity<AjaxResult> historyAnalysis(
             @RequestParam(value = "eventFamily", required = false) String eventFamily,
             @RequestParam(value = "level", required = false) String level,

+ 0 - 2
pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/cockpit/CockpitInfrastructureBaseArchivesController.java

@@ -9,7 +9,6 @@ import com.zksy.common.utils.StringUtils;
 import com.zksy.common.utils.uuid.IdUtils;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
-import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.ExceptionHandler;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.RequestHeader;
@@ -30,7 +29,6 @@ public class CockpitInfrastructureBaseArchivesController {
     }
 
     @GetMapping("/api/cockpit/v1/infrastructure/base-archives")
-    @PreAuthorize("isAuthenticated()")
     public ResponseEntity<AjaxResult> baseArchives(
             @RequestParam(value = "industry", required = false) String industry,
             @RequestParam(value = "facilityType", required = false) String facilityType,

+ 0 - 2
pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/cockpit/CockpitInfrastructureEmergencySupportController.java

@@ -9,7 +9,6 @@ import com.zksy.common.utils.StringUtils;
 import com.zksy.common.utils.uuid.IdUtils;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
-import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.ExceptionHandler;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.RequestHeader;
@@ -30,7 +29,6 @@ public class CockpitInfrastructureEmergencySupportController {
     }
 
     @GetMapping("/api/cockpit/v1/infrastructure/emergency-support")
-    @PreAuthorize("isAuthenticated()")
     public ResponseEntity<AjaxResult> emergencySupport(
             @RequestParam(value = "category", required = false) String category,
             @RequestParam(value = "industry", required = false) String industry,

+ 0 - 2
pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/cockpit/CockpitInfrastructureManagementInformationController.java

@@ -9,7 +9,6 @@ import com.zksy.common.utils.StringUtils;
 import com.zksy.common.utils.uuid.IdUtils;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
-import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.ExceptionHandler;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.RequestHeader;
@@ -30,7 +29,6 @@ public class CockpitInfrastructureManagementInformationController {
     }
 
     @GetMapping("/api/cockpit/v1/infrastructure/management-information")
-    @PreAuthorize("isAuthenticated()")
     public ResponseEntity<AjaxResult> managementInformation(
             @RequestParam(value = "industry", required = false) String industry,
             @RequestParam(value = "organizationCode", required = false) String organizationCode,

+ 0 - 2
pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/cockpit/CockpitInfrastructureOperatingInformationController.java

@@ -9,7 +9,6 @@ import com.zksy.common.utils.StringUtils;
 import com.zksy.common.utils.uuid.IdUtils;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
-import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.ExceptionHandler;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.RequestHeader;
@@ -30,7 +29,6 @@ public class CockpitInfrastructureOperatingInformationController {
     }
 
     @GetMapping("/api/cockpit/v1/infrastructure/operating-information")
-    @PreAuthorize("isAuthenticated()")
     public ResponseEntity<AjaxResult> operatingInformation(
             @RequestParam(value = "industry", required = false) String industry,
             @RequestParam(value = "range", required = false) String range,

+ 0 - 2
pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/cockpit/CockpitMapSnapshotController.java

@@ -11,7 +11,6 @@ import com.zksy.common.utils.StringUtils;
 import com.zksy.common.utils.uuid.IdUtils;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
-import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.ExceptionHandler;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.RequestHeader;
@@ -31,7 +30,6 @@ public class CockpitMapSnapshotController {
     }
 
     @GetMapping("/api/cockpit/v1/map-snapshot")
-    @PreAuthorize("isAuthenticated()")
     public ResponseEntity<AjaxResult> mapSnapshot(
             @RequestParam(value = "page", defaultValue = "1") Integer page,
             @RequestParam(value = "pageSize", defaultValue = "500") Integer pageSize,

+ 0 - 2
pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/cockpit/CockpitMonitoringDistributionController.java

@@ -10,7 +10,6 @@ import com.zksy.common.utils.StringUtils;
 import com.zksy.common.utils.uuid.IdUtils;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
-import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.ExceptionHandler;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.RequestHeader;
@@ -31,7 +30,6 @@ public class CockpitMonitoringDistributionController {
     }
 
     @GetMapping("/api/cockpit/v1/monitoring/distribution")
-    @PreAuthorize("isAuthenticated()")
     public ResponseEntity<AjaxResult> monitoringDistribution(
             @RequestParam(value = "industry", required = false) String industry,
             @RequestParam(value = "deviceType", required = false) String deviceType,

+ 0 - 2
pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/cockpit/CockpitMonitoringOverviewController.java

@@ -7,7 +7,6 @@ import com.zksy.common.utils.StringUtils;
 import com.zksy.common.utils.uuid.IdUtils;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
-import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.ExceptionHandler;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.RequestHeader;
@@ -29,7 +28,6 @@ public class CockpitMonitoringOverviewController {
     }
 
     @GetMapping("/api/cockpit/v1/monitoring/overview")
-    @PreAuthorize("isAuthenticated()")
     public ResponseEntity<AjaxResult> monitoringOverview(
             @RequestParam(value = "industry", required = false) String industry,
             @RequestParam(value = "deviceType", required = false) String deviceType,

+ 0 - 2
pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/cockpit/CockpitMonitoringPointDetailController.java

@@ -13,7 +13,6 @@ import com.zksy.common.utils.StringUtils;
 import com.zksy.common.utils.uuid.IdUtils;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
-import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.ExceptionHandler;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.PathVariable;
@@ -37,7 +36,6 @@ public class CockpitMonitoringPointDetailController {
     }
 
     @GetMapping("/api/cockpit/v1/monitoring-points/{monitoringPointCode}")
-    @PreAuthorize("isAuthenticated()")
     public ResponseEntity<AjaxResult> monitoringPointDetail(
             @PathVariable("monitoringPointCode") String monitoringPointCode,
             @RequestParam(value = "snapshotAt", required = false) String snapshotAt,

+ 0 - 2
pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/cockpit/CockpitMonitoringSituationController.java

@@ -9,7 +9,6 @@ import com.zksy.common.utils.StringUtils;
 import com.zksy.common.utils.uuid.IdUtils;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
-import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.ExceptionHandler;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.RequestHeader;
@@ -30,7 +29,6 @@ public class CockpitMonitoringSituationController {
     }
 
     @GetMapping("/api/cockpit/v1/monitoring/situation")
-    @PreAuthorize("isAuthenticated()")
     public ResponseEntity<AjaxResult> monitoringSituation(
             @RequestParam(value = "industry", required = false) String industry,
             @RequestParam(value = "deviceType", required = false) String deviceType,

+ 0 - 2
pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/cockpit/CockpitMultidimensionalAnalysisController.java

@@ -9,7 +9,6 @@ import com.zksy.common.utils.StringUtils;
 import com.zksy.common.utils.uuid.IdUtils;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
-import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.ExceptionHandler;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.RequestHeader;
@@ -30,7 +29,6 @@ public class CockpitMultidimensionalAnalysisController {
     }
 
     @GetMapping("/api/cockpit/v1/events/multidimensional-analysis")
-    @PreAuthorize("isAuthenticated()")
     public ResponseEntity<AjaxResult> multidimensionalAnalysis(
             @RequestParam(value = "eventFamily", required = false) String eventFamily,
             @RequestParam(value = "industry", required = false) String industry,

+ 0 - 2
pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/cockpit/CockpitOverviewInfrastructureController.java

@@ -8,7 +8,6 @@ import com.zksy.common.utils.StringUtils;
 import com.zksy.common.utils.uuid.IdUtils;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
-import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.ExceptionHandler;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.RequestHeader;
@@ -28,7 +27,6 @@ public class CockpitOverviewInfrastructureController {
     }
 
     @GetMapping("/api/cockpit/v1/overview/infrastructure")
-    @PreAuthorize("isAuthenticated()")
     public ResponseEntity<AjaxResult> overviewInfrastructure(
             @RequestHeader(value = REQUEST_ID_HEADER, required = false) String requestId) {
         return ResponseEntity.ok(AjaxResult.success(

+ 0 - 2
pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/cockpit/CockpitOverviewMonitoringController.java

@@ -9,7 +9,6 @@ import com.zksy.common.utils.StringUtils;
 import com.zksy.common.utils.uuid.IdUtils;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
-import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.ExceptionHandler;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.RequestHeader;
@@ -29,7 +28,6 @@ public class CockpitOverviewMonitoringController {
     }
 
     @GetMapping("/api/cockpit/v1/overview/monitoring")
-    @PreAuthorize("isAuthenticated()")
     public ResponseEntity<AjaxResult> overviewMonitoring(
             @RequestParam(value = "industry", required = false) String industry,
             @RequestHeader(value = REQUEST_ID_HEADER, required = false) String requestId) {

+ 0 - 2
pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/cockpit/CockpitOverviewWarningSettingsController.java

@@ -9,7 +9,6 @@ import com.zksy.common.utils.StringUtils;
 import com.zksy.common.utils.uuid.IdUtils;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
-import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.ExceptionHandler;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.RequestHeader;
@@ -30,7 +29,6 @@ public class CockpitOverviewWarningSettingsController {
     }
 
     @GetMapping("/api/cockpit/v1/overview/warning-settings")
-    @PreAuthorize("isAuthenticated()")
     public ResponseEntity<AjaxResult> warningSettings(
             @RequestParam(value = "mode", required = false) String mode,
             @RequestParam(value = "range", required = false) String range,

+ 0 - 3
pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/cockpit/CockpitTypeMetricsController.java

@@ -11,7 +11,6 @@ import com.zksy.common.utils.StringUtils;
 import com.zksy.common.utils.uuid.IdUtils;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
-import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.ExceptionHandler;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.PathVariable;
@@ -34,7 +33,6 @@ public class CockpitTypeMetricsController {
     }
 
     @GetMapping("/api/cockpit/v1/equipments/{deviceCode}/metrics/current")
-    @PreAuthorize("isAuthenticated()")
     public ResponseEntity<AjaxResult> currentMetrics(
             @PathVariable("deviceCode") String deviceCode,
             @RequestHeader(value = REQUEST_ID_HEADER, required = false) String requestId) {
@@ -44,7 +42,6 @@ public class CockpitTypeMetricsController {
     }
 
     @GetMapping("/api/cockpit/v1/equipments/{deviceCode}/metrics/history")
-    @PreAuthorize("isAuthenticated()")
     public ResponseEntity<AjaxResult> historyMetrics(
             @PathVariable("deviceCode") String deviceCode,
             @RequestParam(value = "startTime") String startTime,

+ 0 - 4
pipe-network-service/zksy-admin/src/main/java/com/zksy/web/controller/cockpit/CockpitWorkOrderReadController.java

@@ -12,7 +12,6 @@ import com.zksy.common.utils.StringUtils;
 import com.zksy.common.utils.uuid.IdUtils;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
-import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.ExceptionHandler;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.PathVariable;
@@ -34,7 +33,6 @@ public class CockpitWorkOrderReadController {
     }
 
     @GetMapping("/api/cockpit/v1/equipments/{deviceCode}/work-orders")
-    @PreAuthorize("isAuthenticated()")
     public ResponseEntity<AjaxResult> deviceWorkOrders(
             @PathVariable("deviceCode") String deviceCode,
             @RequestHeader(value = REQUEST_ID_HEADER, required = false) String requestId) {
@@ -44,7 +42,6 @@ public class CockpitWorkOrderReadController {
     }
 
     @GetMapping("/api/cockpit/v1/events/{eventSource}/{eventCode}/work-orders")
-    @PreAuthorize("isAuthenticated()")
     public ResponseEntity<AjaxResult> eventWorkOrders(
             @PathVariable("eventSource") String eventSource,
             @PathVariable("eventCode") String eventCode,
@@ -57,7 +54,6 @@ public class CockpitWorkOrderReadController {
     }
 
     @GetMapping("/api/cockpit/v1/work-orders/{workOrderCode}/timeline")
-    @PreAuthorize("isAuthenticated()")
     public ResponseEntity<AjaxResult> workOrderTimeline(
             @PathVariable("workOrderCode") String workOrderCode,
             @RequestHeader(value = REQUEST_ID_HEADER, required = false) String requestId) {

+ 9 - 4
pipe-network-service/zksy-admin/src/main/java/com/zksy/web/websocket/DeviceStatusWebSocketHandler.java

@@ -36,18 +36,23 @@ import java.util.concurrent.ConcurrentHashMap;
  * <p>
  * 支持的消息类型(通过 JSON 中的 type 字段区分):
  * <ul>
- *   <li><b>authenticate</b> — 使用登录 JWT 鉴权(工单推送必需)</li>
+ *   <li><b>authenticate</b> — 使用登录 JWT 鉴权(仅工单推送 workOrderReminder 需要;驾驶舱链路不要求)</li>
  *   <li><b>subscribe</b> — 批量订阅设备,后端状态变更时主动推送</li>
  *   <li><b>unsubscribe</b> — 取消订阅设备</li>
- *   <li><b>cockpitSubscribe</b> — 驾驶舱全量状态订阅(scope=all)</li>
+ *   <li><b>cockpitSubscribe</b> — 驾驶舱全量状态订阅(scope=all),<b>免鉴权</b>:连接后可直接发送,
+ *       无需先 authenticate(ADR 0005 修订,2026-09-28;安全性由网络层内网隔离保障)</li>
  *   <li><b>deviceStatus</b> — 单次查询设备状态(兼容旧接口)</li>
- *   <li><b>workOrderReminder</b> — 查询当前用户可见的待处理/超时工单</li>
+ *   <li><b>workOrderReminder</b> — 查询当前用户可见的待处理/超时工单(需先 authenticate)</li>
  *   <li><b>ping</b> — 心跳检测</li>
  * </ul>
  *
  * <h3>协议示例</h3>
  * <pre>
- * // 工单鉴权(连接后首帧发送)
+ * // 驾驶舱接入(2026-09-28 起):连接后**直接** cockpitSubscribe,无需 authenticate 帧
+ * → {"type":"cockpitSubscribe","scope":"all"}
+ * ← {"type":"cockpitSubscribed","scope":"all","serverUpdatedAt":"2026-09-02T10:00:00+08:00"}
+ *
+ * // 工单推送鉴权(仅 workOrderReminder 需要,驾驶舱不用)
  * → {"type":"authenticate","token":"Bearer &lt;JWT&gt;"}
  * ← {"type":"authenticated","success":true,"data":{"userId":10,"deptId":100}}
  *

+ 54 - 0
pipe-network-service/zksy-admin/src/test/java/com/zksy/web/controller/cockpit/CockpitAnonymousAccessSecurityConfigTest.java

@@ -0,0 +1,54 @@
+package com.zksy.web.controller.cockpit;
+
+import org.junit.jupiter.api.Test;
+
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * Locks the confirmed cockpit anonymous-read policy (2026-09-28): GET /api/cockpit/v1/**
+ * is served without authentication, everything else still requires authentication, and
+ * deployment relies on intranet network isolation.
+ *
+ * <p>The full Spring Security chain needs the application context, so this test freezes
+ * the matcher contract directly on {@code zksy-framework/.../SecurityConfig.java}.
+ */
+class CockpitAnonymousAccessSecurityConfigTest {
+
+    private static final Path SECURITY_CONFIG = Path.of(
+            "..", "zksy-framework", "src", "main", "java", "com", "zksy", "framework",
+            "config", "SecurityConfig.java");
+
+    @Test
+    void cockpitPermitAllIsGetScopedAndSingleRule() throws Exception {
+        assertTrue(Files.exists(SECURITY_CONFIG), "SecurityConfig.java must exist at " + SECURITY_CONFIG);
+        List<String> lines = Files.readAllLines(SECURITY_CONFIG);
+
+        long cockpitPermitLines = lines.stream()
+                .filter(line -> line.contains("permitAll") && line.contains("/api/cockpit"))
+                .count();
+        assertEquals(1, cockpitPermitLines, "exactly one cockpit permitAll rule may exist");
+
+        String permitLine = lines.stream()
+                .filter(line -> line.contains("permitAll") && line.contains("/api/cockpit"))
+                .findFirst()
+                .orElseThrow();
+        assertTrue(permitLine.contains("HttpMethod.GET"),
+                "cockpit permitAll must be scoped to GET only");
+        assertTrue(permitLine.contains("\"/api/cockpit/v1/**\""),
+                "cockpit permitAll must cover /api/cockpit/v1/**");
+    }
+
+    @Test
+    void everythingOutsideCockpitGetStillRequiresAuthentication() throws Exception {
+        String source = Files.readString(SECURITY_CONFIG);
+        assertTrue(source.contains(".anyRequest().authenticated()"),
+                "the catch-all rule must still require authentication");
+        assertTrue(source.contains("\"/ws/deviceStatus\""),
+                "the WebSocket endpoint must keep its existing access rule");
+    }
+}

+ 1 - 1
pipe-network-service/zksy-admin/src/test/java/com/zksy/web/controller/cockpit/CockpitAuthenticationEntryPointTest.java

@@ -18,7 +18,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
 class CockpitAuthenticationEntryPointTest {
 
     @Test
-    void unauthenticatedCockpitRequestReturnsUnauthorizedHttpStatusAndTypedError() throws Exception {
+    void securityNetReturnsUnauthorizedHttpStatusAndTypedErrorForUnauthenticatedCockpitRequests() throws Exception {
         MockHttpServletRequest request = new MockHttpServletRequest("GET", "/api/cockpit/v1/foundation");
         request.addHeader("X-Request-Id", "foundation-request-401");
         MockHttpServletResponse response = new MockHttpServletResponse();

+ 0 - 6
pipe-network-service/zksy-admin/src/test/java/com/zksy/web/controller/cockpit/CockpitCurrentEventsControllerTest.java

@@ -6,7 +6,6 @@ import com.zksy.cockpit.event.CockpitCurrentEventsSnapshot;
 import com.zksy.cockpit.foundation.CockpitReadException;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
-import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.test.web.servlet.MockMvc;
 import org.springframework.test.web.servlet.setup.MockMvcBuilders;
 import org.springframework.web.bind.annotation.DeleteMapping;
@@ -320,11 +319,6 @@ class CockpitCurrentEventsControllerTest {
         assertTrue(Arrays.stream(CockpitCurrentEventsController.class.getDeclaredMethods())
                 .filter(this::hasRequestMappingAnnotation)
                 .allMatch(CockpitCurrentEventsControllerTest::isReadOnlyMappedMethod));
-
-        assertTrue(Arrays.stream(CockpitCurrentEventsController.class.getDeclaredMethods())
-                .anyMatch(method -> method.isAnnotationPresent(PreAuthorize.class)
-                        && "isAuthenticated()".equals(
-                                method.getAnnotation(PreAuthorize.class).value())));
     }
 
     private boolean hasRequestMappingAnnotation(Method method) {

+ 0 - 6
pipe-network-service/zksy-admin/src/test/java/com/zksy/web/controller/cockpit/CockpitDeviceBoundaryControllerTest.java

@@ -193,12 +193,6 @@ class CockpitDeviceBoundaryControllerTest {
                         || method.isAnnotationPresent(org.springframework.web.bind.annotation.PatchMapping.class)
                         || method.isAnnotationPresent(org.springframework.web.bind.annotation.DeleteMapping.class))
                 .allMatch(CockpitDeviceBoundaryControllerTest::isReadOnlyMappedMethod));
-
-        assertTrue(Arrays.stream(CockpitDeviceBoundaryController.class.getDeclaredMethods())
-                .anyMatch(method -> method.isAnnotationPresent(org.springframework.security.access.prepost.PreAuthorize.class)
-                        && method.getAnnotation(org.springframework.security.access.prepost.PreAuthorize.class)
-                        .value()
-                        .equals("isAuthenticated()")));
     }
 
     private static boolean isReadOnlyMappedMethod(java.lang.reflect.Method method) {

+ 0 - 7
pipe-network-service/zksy-admin/src/test/java/com/zksy/web/controller/cockpit/CockpitEquipmentArchiveControllerTest.java

@@ -183,13 +183,6 @@ class CockpitEquipmentArchiveControllerTest {
         assertTrue(java.util.Arrays.stream(CockpitEquipmentArchiveController.class.getDeclaredMethods())
                 .filter(this::hasRequestMappingAnnotation)
                 .allMatch(CockpitEquipmentArchiveControllerTest::isReadOnlyMappedMethod));
-
-        assertTrue(java.util.Arrays.stream(CockpitEquipmentArchiveController.class.getDeclaredMethods())
-                .anyMatch(method -> method.isAnnotationPresent(
-                        org.springframework.security.access.prepost.PreAuthorize.class)
-                        && method.getAnnotation(org.springframework.security.access.prepost.PreAuthorize.class)
-                        .value()
-                        .equals("isAuthenticated()")));
     }
 
     private boolean hasRequestMappingAnnotation(Method method) {

+ 0 - 4
pipe-network-service/zksy-admin/src/test/java/com/zksy/web/controller/cockpit/CockpitFoundationControllerTest.java

@@ -91,10 +91,6 @@ class CockpitFoundationControllerTest {
                         || method.isAnnotationPresent(org.springframework.web.bind.annotation.PatchMapping.class)
                         || method.isAnnotationPresent(org.springframework.web.bind.annotation.DeleteMapping.class))
                 .allMatch(CockpitFoundationControllerTest::isReadOnlyMappedMethod));
-
-        assertTrue(Arrays.stream(controller.getClass().getDeclaredMethods())
-                .anyMatch(method -> method.isAnnotationPresent(org.springframework.security.access.prepost.PreAuthorize.class)
-                        && method.getAnnotation(org.springframework.security.access.prepost.PreAuthorize.class).value().equals("isAuthenticated()")));
     }
 
     private static boolean isReadOnlyMappedMethod(Method method) {

+ 0 - 4
pipe-network-service/zksy-admin/src/test/java/com/zksy/web/controller/cockpit/CockpitFreshnessSemanticsControllerTest.java

@@ -197,10 +197,6 @@ class CockpitFreshnessSemanticsControllerTest {
         }
         assertTrue(Arrays.stream(CockpitFreshnessController.class.getDeclaredMethods())
                 .filter(method -> method.isAnnotationPresent(org.springframework.web.bind.annotation.GetMapping.class))
-                .allMatch(method -> method.isAnnotationPresent(org.springframework.security.access.prepost.PreAuthorize.class)
-                        && method.getAnnotation(org.springframework.security.access.prepost.PreAuthorize.class)
-                        .value()
-                        .equals("isAuthenticated()")));
     }
 
     private void assertTrue(boolean condition) {

+ 0 - 6
pipe-network-service/zksy-admin/src/test/java/com/zksy/web/controller/cockpit/CockpitHandlingEfficiencyControllerTest.java

@@ -6,7 +6,6 @@ import com.zksy.cockpit.event.CockpitHandlingEfficiencySnapshot;
 import com.zksy.cockpit.foundation.CockpitReadException;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
-import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.test.web.servlet.MockMvc;
 import org.springframework.test.web.servlet.setup.MockMvcBuilders;
 import org.springframework.web.bind.annotation.DeleteMapping;
@@ -316,11 +315,6 @@ class CockpitHandlingEfficiencyControllerTest {
         assertTrue(Arrays.stream(CockpitHandlingEfficiencyController.class.getDeclaredMethods())
                 .filter(this::hasRequestMappingAnnotation)
                 .allMatch(CockpitHandlingEfficiencyControllerTest::isReadOnlyMappedMethod));
-
-        assertTrue(Arrays.stream(CockpitHandlingEfficiencyController.class.getDeclaredMethods())
-                .anyMatch(method -> method.isAnnotationPresent(PreAuthorize.class)
-                        && "isAuthenticated()".equals(
-                                method.getAnnotation(PreAuthorize.class).value())));
     }
 
     private boolean hasRequestMappingAnnotation(Method method) {

+ 0 - 6
pipe-network-service/zksy-admin/src/test/java/com/zksy/web/controller/cockpit/CockpitHistoryAnalysisControllerTest.java

@@ -7,7 +7,6 @@ import com.zksy.cockpit.event.CockpitHistoryAnalysisSnapshot;
 import com.zksy.cockpit.foundation.CockpitReadException;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
-import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.test.web.servlet.MockMvc;
 import org.springframework.test.web.servlet.setup.MockMvcBuilders;
 import org.springframework.web.bind.annotation.DeleteMapping;
@@ -265,11 +264,6 @@ class CockpitHistoryAnalysisControllerTest {
         assertTrue(Arrays.stream(CockpitHistoryAnalysisController.class.getDeclaredMethods())
                 .filter(this::hasRequestMappingAnnotation)
                 .allMatch(CockpitHistoryAnalysisControllerTest::isReadOnlyMappedMethod));
-
-        assertTrue(Arrays.stream(CockpitHistoryAnalysisController.class.getDeclaredMethods())
-                .anyMatch(method -> method.isAnnotationPresent(PreAuthorize.class)
-                        && "isAuthenticated()".equals(
-                                method.getAnnotation(PreAuthorize.class).value())));
     }
 
     private boolean hasRequestMappingAnnotation(Method method) {

+ 0 - 7
pipe-network-service/zksy-admin/src/test/java/com/zksy/web/controller/cockpit/CockpitInfrastructureBaseArchivesControllerTest.java

@@ -258,13 +258,6 @@ class CockpitInfrastructureBaseArchivesControllerTest {
                         || method.isAnnotationPresent(org.springframework.web.bind.annotation.PatchMapping.class)
                         || method.isAnnotationPresent(org.springframework.web.bind.annotation.DeleteMapping.class))
                 .allMatch(CockpitInfrastructureBaseArchivesControllerTest::isReadOnlyMappedMethod));
-
-        assertTrue(Arrays.stream(CockpitInfrastructureBaseArchivesController.class.getDeclaredMethods())
-                .anyMatch(method -> method.isAnnotationPresent(
-                        org.springframework.security.access.prepost.PreAuthorize.class)
-                        && method.getAnnotation(org.springframework.security.access.prepost.PreAuthorize.class)
-                        .value()
-                        .equals("isAuthenticated()")));
     }
 
     private static CockpitInfrastructureBaseArchivesSnapshot fixture() {

+ 0 - 7
pipe-network-service/zksy-admin/src/test/java/com/zksy/web/controller/cockpit/CockpitInfrastructureEmergencySupportControllerTest.java

@@ -6,7 +6,6 @@ import com.zksy.cockpit.infrastructure.CockpitInfrastructureEmergencySupportServ
 import com.zksy.cockpit.infrastructure.CockpitInfrastructureEmergencySupportSnapshot;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
-import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.test.web.servlet.MockMvc;
 import org.springframework.test.web.servlet.setup.MockMvcBuilders;
 import org.springframework.web.bind.annotation.DeleteMapping;
@@ -268,12 +267,6 @@ class CockpitInfrastructureEmergencySupportControllerTest {
                         && !method.isAnnotationPresent(DeleteMapping.class));
             }
         }
-
-        assertTrue(Arrays.stream(
-                        CockpitInfrastructureEmergencySupportController.class.getDeclaredMethods())
-                .anyMatch(method -> method.isAnnotationPresent(PreAuthorize.class)
-                        && "isAuthenticated()".equals(
-                                method.getAnnotation(PreAuthorize.class).value())));
     }
 
     private static CockpitInfrastructureEmergencySupportSnapshot fixture() {

+ 0 - 7
pipe-network-service/zksy-admin/src/test/java/com/zksy/web/controller/cockpit/CockpitInfrastructureManagementInformationControllerTest.java

@@ -6,7 +6,6 @@ import com.zksy.cockpit.infrastructure.CockpitInfrastructureManagementInformatio
 import com.zksy.cockpit.infrastructure.CockpitInfrastructureManagementInformationSnapshot;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
-import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.test.web.servlet.MockMvc;
 import org.springframework.test.web.servlet.setup.MockMvcBuilders;
 import org.springframework.web.bind.annotation.DeleteMapping;
@@ -265,12 +264,6 @@ class CockpitInfrastructureManagementInformationControllerTest {
                         && !method.isAnnotationPresent(DeleteMapping.class));
             }
         }
-
-        assertTrue(Arrays.stream(
-                        CockpitInfrastructureManagementInformationController.class.getDeclaredMethods())
-                .anyMatch(method -> method.isAnnotationPresent(PreAuthorize.class)
-                        && "isAuthenticated()".equals(
-                                method.getAnnotation(PreAuthorize.class).value())));
     }
 
     private static CockpitInfrastructureManagementInformationSnapshot fixture() {

+ 0 - 7
pipe-network-service/zksy-admin/src/test/java/com/zksy/web/controller/cockpit/CockpitInfrastructureOperatingInformationControllerTest.java

@@ -6,7 +6,6 @@ import com.zksy.cockpit.infrastructure.CockpitInfrastructureOperatingInformation
 import com.zksy.cockpit.infrastructure.CockpitInfrastructureOperatingInformationSnapshot;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
-import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.test.web.servlet.MockMvc;
 import org.springframework.test.web.servlet.setup.MockMvcBuilders;
 import org.springframework.web.bind.annotation.DeleteMapping;
@@ -279,14 +278,8 @@ class CockpitInfrastructureOperatingInformationControllerTest {
                         && !method.isAnnotationPresent(DeleteMapping.class));
             }
         }
-
-        assertTrue(Arrays.stream(CockpitInfrastructureOperatingInformationController.class.getDeclaredMethods())
-                .anyMatch(method -> method.isAnnotationPresent(PreAuthorize.class)
-                        && "isAuthenticated()".equals(
-                                method.getAnnotation(PreAuthorize.class).value())));
     }
 
-
     private static CockpitInfrastructureOperatingInformationSnapshot fixture() {
         return new CockpitInfrastructureOperatingInformationSnapshot(
                 "2026-09-02T09:59:50+08:00",

+ 0 - 6
pipe-network-service/zksy-admin/src/test/java/com/zksy/web/controller/cockpit/CockpitMapSnapshotControllerTest.java

@@ -381,12 +381,6 @@ class CockpitMapSnapshotControllerTest {
         assertTrue(Arrays.stream(CockpitMapSnapshotController.class.getDeclaredMethods())
                 .filter(this::hasRequestMappingAnnotation)
                 .allMatch(CockpitMapSnapshotControllerTest::isReadOnlyMappedMethod));
-
-        assertTrue(Arrays.stream(CockpitMapSnapshotController.class.getDeclaredMethods())
-                .anyMatch(method -> method.isAnnotationPresent(org.springframework.security.access.prepost.PreAuthorize.class)
-                        && method.getAnnotation(org.springframework.security.access.prepost.PreAuthorize.class)
-                        .value()
-                        .equals("isAuthenticated()")));
     }
 
     private boolean hasRequestMappingAnnotation(Method method) {

+ 0 - 6
pipe-network-service/zksy-admin/src/test/java/com/zksy/web/controller/cockpit/CockpitMonitoringDistributionControllerTest.java

@@ -11,7 +11,6 @@ import com.zksy.cockpit.point.CockpitMonitoringPointDeviceSnapshot;
 import com.zksy.cockpit.point.CockpitMonitoringPointStatus;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
-import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.test.web.servlet.MockMvc;
 import org.springframework.test.web.servlet.setup.MockMvcBuilders;
 import org.springframework.web.bind.annotation.DeleteMapping;
@@ -311,11 +310,6 @@ class CockpitMonitoringDistributionControllerTest {
         assertTrue(Arrays.stream(CockpitMonitoringDistributionController.class.getDeclaredMethods())
                 .filter(this::hasRequestMappingAnnotation)
                 .allMatch(CockpitMonitoringDistributionControllerTest::isReadOnlyMappedMethod));
-
-        assertTrue(Arrays.stream(CockpitMonitoringDistributionController.class.getDeclaredMethods())
-                .anyMatch(method -> method.isAnnotationPresent(PreAuthorize.class)
-                        && "isAuthenticated()".equals(
-                                method.getAnnotation(PreAuthorize.class).value())));
     }
 
     @Test

+ 0 - 6
pipe-network-service/zksy-admin/src/test/java/com/zksy/web/controller/cockpit/CockpitMonitoringOverviewControllerTest.java

@@ -8,7 +8,6 @@ import com.zksy.cockpit.monitoring.CockpitMonitoringOverviewSnapshot;
 import com.zksy.cockpit.monitoring.CockpitMonitoringOverviewResponse;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
-import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.test.web.servlet.MockMvc;
 import org.springframework.test.web.servlet.setup.MockMvcBuilders;
 import org.springframework.web.bind.annotation.DeleteMapping;
@@ -238,11 +237,6 @@ class CockpitMonitoringOverviewControllerTest {
                         && !method.isAnnotationPresent(DeleteMapping.class));
             }
         }
-
-        assertTrue(Arrays.stream(CockpitMonitoringOverviewController.class.getDeclaredMethods())
-                .anyMatch(method -> method.isAnnotationPresent(PreAuthorize.class)
-                        && "isAuthenticated()".equals(
-                                method.getAnnotation(PreAuthorize.class).value())));
     }
 
     private static CockpitMonitoringOverviewSnapshot fixture() {

+ 0 - 6
pipe-network-service/zksy-admin/src/test/java/com/zksy/web/controller/cockpit/CockpitMonitoringPointDetailControllerTest.java

@@ -312,12 +312,6 @@ class CockpitMonitoringPointDetailControllerTest {
         assertTrue(Arrays.stream(CockpitMonitoringPointDetailController.class.getDeclaredMethods())
                 .filter(this::hasRequestMappingAnnotation)
                 .allMatch(CockpitMonitoringPointDetailControllerTest::isReadOnlyMappedMethod));
-
-        assertTrue(Arrays.stream(CockpitMonitoringPointDetailController.class.getDeclaredMethods())
-                .anyMatch(method -> method.isAnnotationPresent(org.springframework.security.access.prepost.PreAuthorize.class)
-                        && method.getAnnotation(org.springframework.security.access.prepost.PreAuthorize.class)
-                        .value()
-                        .equals("isAuthenticated()")));
     }
 
     private boolean hasRequestMappingAnnotation(Method method) {

+ 0 - 6
pipe-network-service/zksy-admin/src/test/java/com/zksy/web/controller/cockpit/CockpitMonitoringSituationControllerTest.java

@@ -8,7 +8,6 @@ import com.zksy.cockpit.monitoring.CockpitMonitoringSituationService;
 import com.zksy.cockpit.monitoring.CockpitMonitoringSituationSnapshot;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
-import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.test.web.servlet.MockMvc;
 import org.springframework.test.web.servlet.setup.MockMvcBuilders;
 import org.springframework.web.bind.annotation.DeleteMapping;
@@ -326,11 +325,6 @@ class CockpitMonitoringSituationControllerTest {
         assertTrue(Arrays.stream(CockpitMonitoringSituationController.class.getDeclaredMethods())
                 .filter(this::hasRequestMappingAnnotation)
                 .allMatch(CockpitMonitoringSituationControllerTest::isReadOnlyMappedMethod));
-
-        assertTrue(Arrays.stream(CockpitMonitoringSituationController.class.getDeclaredMethods())
-                .anyMatch(method -> method.isAnnotationPresent(PreAuthorize.class)
-                        && "isAuthenticated()".equals(
-                                method.getAnnotation(PreAuthorize.class).value())));
     }
 
     @Test

+ 0 - 6
pipe-network-service/zksy-admin/src/test/java/com/zksy/web/controller/cockpit/CockpitMultidimensionalAnalysisControllerTest.java

@@ -7,7 +7,6 @@ import com.zksy.cockpit.event.CockpitMultidimensionalAnalysisSnapshot;
 import com.zksy.cockpit.foundation.CockpitReadException;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
-import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.test.web.servlet.MockMvc;
 import org.springframework.test.web.servlet.setup.MockMvcBuilders;
 import org.springframework.web.bind.annotation.DeleteMapping;
@@ -343,11 +342,6 @@ class CockpitMultidimensionalAnalysisControllerTest {
         assertTrue(Arrays.stream(CockpitMultidimensionalAnalysisController.class.getDeclaredMethods())
                 .filter(this::hasRequestMappingAnnotation)
                 .allMatch(CockpitMultidimensionalAnalysisControllerTest::isReadOnlyMappedMethod));
-
-        assertTrue(Arrays.stream(CockpitMultidimensionalAnalysisController.class.getDeclaredMethods())
-                .anyMatch(method -> method.isAnnotationPresent(PreAuthorize.class)
-                        && "isAuthenticated()".equals(
-                                method.getAnnotation(PreAuthorize.class).value())));
     }
 
     private boolean hasRequestMappingAnnotation(Method method) {

+ 0 - 7
pipe-network-service/zksy-admin/src/test/java/com/zksy/web/controller/cockpit/CockpitOverviewInfrastructureControllerTest.java

@@ -210,13 +210,6 @@ class CockpitOverviewInfrastructureControllerTest {
                         || method.isAnnotationPresent(org.springframework.web.bind.annotation.PatchMapping.class)
                         || method.isAnnotationPresent(org.springframework.web.bind.annotation.DeleteMapping.class))
                 .allMatch(CockpitOverviewInfrastructureControllerTest::isReadOnlyMappedMethod));
-
-        assertTrue(Arrays.stream(CockpitOverviewInfrastructureController.class.getDeclaredMethods())
-                .anyMatch(method -> method.isAnnotationPresent(
-                        org.springframework.security.access.prepost.PreAuthorize.class)
-                        && method.getAnnotation(org.springframework.security.access.prepost.PreAuthorize.class)
-                        .value()
-                        .equals("isAuthenticated()")));
     }
 
     private static CockpitOverviewInfrastructureSnapshot fixture() {

+ 0 - 6
pipe-network-service/zksy-admin/src/test/java/com/zksy/web/controller/cockpit/CockpitOverviewMonitoringControllerTest.java

@@ -125,12 +125,6 @@ class CockpitOverviewMonitoringControllerTest {
                         || method.isAnnotationPresent(org.springframework.web.bind.annotation.PatchMapping.class)
                         || method.isAnnotationPresent(org.springframework.web.bind.annotation.DeleteMapping.class))
                 .allMatch(method -> method.isAnnotationPresent(GetMapping.class)));
-
-        assertTrue(Arrays.stream(CockpitOverviewMonitoringController.class.getDeclaredMethods())
-                .anyMatch(method -> method.isAnnotationPresent(
-                        org.springframework.security.access.prepost.PreAuthorize.class)
-                        && "isAuthenticated()".equals(method.getAnnotation(
-                                org.springframework.security.access.prepost.PreAuthorize.class).value())));
     }
 
     private static CockpitOverviewMonitoringSnapshot fixture() {

+ 0 - 6
pipe-network-service/zksy-admin/src/test/java/com/zksy/web/controller/cockpit/CockpitOverviewWarningSettingsControllerTest.java

@@ -6,7 +6,6 @@ import com.zksy.cockpit.overview.CockpitOverviewWarningSettingsService;
 import com.zksy.cockpit.overview.CockpitOverviewWarningSettingsSnapshot;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
-import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.PutMapping;
@@ -225,11 +224,6 @@ class CockpitOverviewWarningSettingsControllerTest {
                         && !method.isAnnotationPresent(PutMapping.class)
                         && !method.isAnnotationPresent(PatchMapping.class)
                         && !method.isAnnotationPresent(DeleteMapping.class)));
-
-        assertTrue(Arrays.stream(CockpitOverviewWarningSettingsController.class.getDeclaredMethods())
-                .anyMatch(method -> method.isAnnotationPresent(PreAuthorize.class)
-                        && "isAuthenticated()".equals(method.getAnnotation(
-                                PreAuthorize.class).value())));
     }
 
     private static void assertTrue(boolean condition) {

+ 17 - 0
pipe-network-service/zksy-admin/src/test/java/com/zksy/web/websocket/DeviceStatusCockpitWebSocketHandlerTest.java

@@ -68,6 +68,23 @@ class DeviceStatusCockpitWebSocketHandlerTest {
         assertTrue(payload.containsKey("serverUpdatedAt"));
     }
 
+    @Test
+    void cockpitSubscribeWorksWithoutAnyAuthenticateFrame() throws Exception {
+        // ADR 0005 修订(2026-09-28):驾驶舱 WS 免鉴权 —— 连接后直接 cockpitSubscribe
+        // 即可订阅成功,不要求也不依赖 authenticate 帧;本会话从未发送 authenticate。
+        handler.afterConnectionEstablished(session);
+        handler.handleTextMessage(session, new TextMessage(
+                "{\"type\":\"cockpitSubscribe\",\"scope\":\"all\"}"));
+
+        ArgumentCaptor<TextMessage> response = ArgumentCaptor.forClass(TextMessage.class);
+        verify(session).sendMessage(response.capture());
+        JSONObject payload = JSONObject.parseObject(response.getValue().getPayload());
+        assertEquals("cockpitSubscribed", payload.getString("type"));
+        assertEquals("all", payload.getString("scope"));
+        assertEquals("ws-session-1", subscriptionManager.getAllScopeSessions().iterator().next(),
+                "未鉴权会话也应进入全量订阅集合");
+    }
+
     @Test
     void cockpitAllScopeReceivesChangedDeviceDelta() throws Exception {
         handler.afterConnectionEstablished(session);

+ 3 - 2
pipe-network-service/zksy-framework/src/main/java/com/zksy/framework/config/SecurityConfig.java

@@ -123,8 +123,9 @@ public class SecurityConfig
             // 注解标记允许匿名访问的url
             .authorizeHttpRequests((requests) -> {
                 permitAllUrl.getUrls().forEach(url -> requests.antMatchers(url).permitAll());
-                // 对于登录login 注册register 验证码captchaImage 允许匿名访问
-                requests.antMatchers("/login", "/register", "/captchaImage","/**/visualization/**","/ws/deviceStatus").permitAll()
+                requests.antMatchers(HttpMethod.GET, "/api/cockpit/v1/**").permitAll()
+                    // 对于登录login 注册register 验证码captchaImage 允许匿名访问
+                    .antMatchers("/login", "/register", "/captchaImage","/**/visualization/**","/ws/deviceStatus").permitAll()
                     // 静态资源,可匿名访问
                     .antMatchers(HttpMethod.GET, "/", "/*.html", "/**/*.html", "/**/*.css", "/**/*.js", "/profile/**").permitAll()
                     .antMatchers("/swagger-ui.html", "/swagger-resources/**", "/webjars/**", "/*/api-docs", "/druid/**").permitAll()