# Cockpit API auth without a login page Type: grilling Status: resolved Labels: wayfinder:grilling Blocked by: None ## 进度:100% 已关闭。Q1–Q5 全部确认。凭据由部署层注入请求头;401 只标相关分区为数据不可用;默认 mock;WebSocket 先 authenticate 再 cockpitSubscribe。 ## 后端事实(只读核实) 来源:`E:\pipe-ner-server\pipe-network-service`。 - **框架**:Spring Security + JWT,`SessionCreationPolicy.STATELESS`,CSRF 已禁用。 - **REST 凭据**:`TokenService.getToken()` 只从请求头读取,头名由 `bootstrap.yaml` 的 `token.header` 给出,值为 `Authorization`;`Bearer ` 前缀会被剥离。 - **驾驶舱 REST 不在白名单**:`SecurityConfig` 的 permitAll 只含 `/login`、`/register`、`/captchaImage`、`/**/visualization/**`、`/ws/deviceStatus`,其余 `.anyRequest().authenticated()`。因此 `/api/cockpit/v1/**` 必须带 token。 - **未认证响应**:`/api/cockpit/**` 命中 `CockpitAuthenticationEntryPoint`,返回 HTTP 401,body 为 `AjaxResult(code=401, msg="驾驶舱接口需要认证", data=CockpitErrorPayload{category=UNAUTHORIZED, requestId, serverTime})`。 - **token 来源**:只有 `POST /login` 能签发 token;驾驶舱首期不做登录页,所以 token 必须由外部提供。 - **WebSocket**:`/ws/deviceStatus` 握手层是 permitAll;客户端可发 `{"type":"authenticate","token":"..."}` 完成 JWT/Redis 校验;而 `handleCockpitSubscribe` 当前**只校验 `scope`,不校验身份**,所以 `cockpitSubscribe` 今天无需 token 即可订阅。 - **CORS**:`ResourcesConfig.corsFilter()` 设 `addAllowedOriginPattern("*")` 并允许全部头与方法,但**未开启 allowCredentials**。因此跨域 Cookie 会话不可行,请求头方案可行。 - **未发现开发例外**:没有任何 profile 或调试头能让驾驶舱 REST 免鉴权。 事实面结论:REST 必须带 `Authorization: Bearer `;token 只能由 `/login` 签发;驾驶舱没有登录页,所以凭据只能由部署层提供。跨域 Cookie 不可行。 ## Question 驾驶舱专用接口要求 `isAuthenticated()`,但首期明确不做登录页。mock 和真实联调分别怎么拿到凭据,失败时又如何只显示数据不可用? 候选: - 首期只走契约化 mock;真实接口等独立登录方案 - 部署注入已有 token(cookie / header / 网关),前端不管理账号 - 开发环境使用固定调试头或反向代理注入,生产仍由网关注入 约束:前端不硬编码账号或密钥;缺失凭据时显示数据不可用,不跳登录页。 ## Answer **凭据承载(Q4=A):** 部署层把 `Authorization: Bearer ` 注入到发往后端 `/api/cockpit/v1/**` 的请求:生产由网关/反向代理在转发时补头;开发环境用同一机制的本地反代或部署配置提供的只读 token。适配层统一附加请求头,页面与 store 不触碰 token,前端不保存账号密码、不建登录页。不采用前端内置固定 token。Cookie 会话不可行(后端 CORS 未开 allowCredentials)。 **未认证体验(Q1=A):** 收到 401 或其他凭据类失败时,只把受影响的数据分区标为「数据不可用」,保留已成功分区与地图骨架;不跳登录页、不整页遮罩、不静默显示空数据。顶部显示一次性数据不可用提示。 **默认数据源(Q2=A):** 默认走契约化 mock;真实接口必须在部署配置里显式开启(mock/real 走同一适配层,切换只改配置)。 **轮询(Q3=A):** WebSocket 失败降级轮询与新鲜度语义不在本票定义,归实现票 16。 **WebSocket 鉴权(Q5=A):** 连接 `/ws/deviceStatus` 后、发送 `cockpitSubscribe` 前先发 `{"type":"authenticate","token":"..."}`,token 与 REST 同源。后端当前 `cockpitSubscribe` 只校验 scope 不校验身份,authenticate 是为后端日后收紧时的前向兼容。鉴权失败只把状态推送标为不可用,REST 快照照常显示。