# 验证环境专用调试发令牌接口(`@Profile("verify")`) ## Status accepted ## Context 内网验证环境(172.16.204.61 全套 docker-compose 自包含部署)需要快速换取合法 JWT 来调试受鉴权保护的接口(`/api/file/**` 等 `authenticated()` 端点)。唯一的正式登录入口是钉钉扫码(`/api/auth/login/dingtalk`),依赖出站访问 `api.dingtalk.com` 且 authCode 短时效,反复重建环境时逐次扫码换令牌成本高。 ## Decision 新增 `POST /api/auth/debug/token?userId=xxx`:从 `crm_auth_user` 查用户,组装 `AuthLoginUser`(复用 `TokenService.createToken`)签发标准 JWT。该端点**仅在 `verify` profile 下注册**,三层护栏叠加: 1. **`@Profile("verify")`** —— 生产不激活 verify profile 时 Controller Bean 不注册,端点不存在。 2. **verify 专属放行** —— `application-verify.yml` 把该端点与 `/api/file/thumbnail`(按缩略图 spec §6.1 本应无门禁)加入 `crm.auth.ignore-urls`。 3. **启动期 WARN 告警** —— verify profile 激活时打印显眼日志,留审计痕迹。 生产部署不带 `verify` profile,端点与放行规则均不生效。 ## Considered Options - **离线 JWT 签发小工具(`main()`/脚本)** —— 天然不进 HTTP 攻击面,最安全;但每次需本地跑、复制粘贴,便利性差。**被否**:团队选择就近的 HTTP 接口便利性。 - **不加任何接口,纯靠钉钉首登换 7 天 JWT** —— 零新增代码、零后门;但反复重建验证环境时痛点明显。**被否**:同上。 ## Consequences - 这是一个**刻意保留的后门接口**。护栏押在"生产永不激活 verify profile"这一约束上——CI/CD 与部署脚本必须保证生产 profile 绝不含 `verify`。 - 未来读代码者若见到"给 userId 即发 JWT"的端点,本 ADR 解释其存在理由与隔离机制,避免误判为漏洞或误删。