07-cockpit-api-auth-without-login.md 4.0 KB

# 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 <JWT>;token 只能由 /login 签发;驾驶舱没有登录页,所以凭据只能由部署层提供。跨域 Cookie 不可行。

Question

驾驶舱专用接口要求 isAuthenticated(),但首期明确不做登录页。mock 和真实联调分别怎么拿到凭据,失败时又如何只显示数据不可用?

候选:

  • 首期只走契约化 mock;真实接口等独立登录方案
  • 部署注入已有 token(cookie / header / 网关),前端不管理账号
  • 开发环境使用固定调试头或反向代理注入,生产仍由网关注入

约束:前端不硬编码账号或密钥;缺失凭据时显示数据不可用,不跳登录页。

Answer

凭据承载(Q4=A): 部署层把 Authorization: Bearer <JWT> 注入到发往后端 /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 快照照常显示。