概述 — 这套机制做什么
InfraGuard 有 两个认证平面:
- Web 控制台 = 会话登录。 运维人员用
login_id+password登录,AuthController会创建一个自定义会话。此后每个控制台页面都会经过infr.auth中间件(InfrAuth),它在校验会话的同时 在每次请求时都从数据库重新读取用户的级别、姓名和语言,以保持会话副本同步。 - 外部接入 API = 无状态的 API 密钥 Bearer 认证。 当外部应用上报事件、备份结果或安全扫描时,它通过
infr.apikey中间件用 API 密钥令牌(Authorization: Bearer …或X-Infr-Api-Key)认证,而不是会话。没有会话,也没有 CSRF。
主要特性:
- 会话重新同步 — 管理员修改用户的级别、姓名或语言后,从下一次请求起 生效,无需重新登录。
- URL 级别的强制 — 仅仅隐藏菜单项是不够的。
/security、/backup和/admin等路由组通过infr.level:N中间件直接在 URL 上强制最低级别。 - 破坏性操作 = 级别复核 + 审计 — 封禁、恢复、修改设置等操作由写入级别门槛、
infr_audit_log记录和界面确认共同保护。
何时使用
- 你第一次登录控制台,或者突然被弹回
/login而想弄清原因。 - 你正在把外部 Laravel 应用接入 事件接入 API 或 备份结果回传,需要知道用哪个请求头进行认证。
- 管理员修改了级别/姓名/语言,而你需要解释为什么不必重新登录。
页面构成
登录页面(/login)是一张简单的卡片。
| 元素 | 说明 |
|---|---|
| 品牌 · 标语 | Infra**Guard** 徽标 +「融合基础设施运维 · 防御 · 韧性控制台」 |
| 登录账号 | login_id 输入框。autocomplete=username,自动聚焦 |
| 密码 | password 输入框 + 眼睛图标的显示/隐藏切换 |
| 错误横幅 | 失败时:「账号或密码不正确。」 |
隐藏字段 next |
登录后的返回路径(必须以 / 开头) |
| 提交按钮 | 「登录 →」 |
登录表单携带标准的 Blade
@csrf令牌。控制台由会话 + CSRF 保护,界面上不会另外索要 bearer 令牌。
如何使用
登录
- 打开
/login(如果已登录,会被重定向到/)。 - 输入你的
Login ID和Password。- 校验规则:
login_id最长 60 个字符,password为 4–120 个字符。
- 校验规则:
- 点击 登录。服务器按
login_id+is_close=0在infr_user中查找账号并校验密码。 - 成功后会话被重新生成(
session()->regenerate()),并跳转到next(或/)。
登录成功会写入以下 会话键:
| 会话键 | 值 |
|---|---|
infr_user_id |
infr_user.id(判断是否已认证的依据) |
infr_login_id |
登录账号 |
infr_user_level |
user_level(整数) — RBAC 级别 |
infr_name |
显示名称 |
infr_lang |
显示语言(默认为 ko) |
同时会将 last_log 列更新为当前时间。
退出登录
- 使用顶栏用户下拉菜单中的 退出登录 项(或
POST /logout)。 - 服务器会清除
infr_user_id、使会话失效、重新生成 CSRF 令牌,并重定向到/login。
会话有效期与每次请求的重新同步
每个控制台页面都会经过 infr.auth(InfrAuth),它在每次请求时:
- 用会话中的
infr_user_id重新加载用户(热路径,只取最少的列:id · login_id · name · email · user_level · role_id · team_id · lang · avatar · is_close)。 - 如果账号不存在或
is_close=1(已停用),就销毁会话并重定向到/login(JSON 请求则返回 401)。 - 用数据库中的值覆盖
infr_user_level·infr_name·infr_lang— 因此管理员对级别/姓名/语言的修改会从下一次请求起生效,无需重新登录。 - 把用户模型放入
infr_user请求属性,并通过app()->setLocale()设置显示语言。 - 调用
OpsPolicy::markPresence()记录 在线状态(运维策略的节能模式用它判断「此刻是否有控制台用户在活动」,并据此控制数据库采集 — 参见 运维策略)。
会话有效期遵循 SESSION_LIFETIME(默认 120 分钟)。
控制台登录没有单独的「记住我」复选框。会话只在上述有效期内保持,过期后必须重新登录。
外部接入 API 认证(Bearer 密钥)
当外部应用向 /api/v1/* 发送请求时,它使用 API 密钥 认证(无状态 — 没有会话,也没有 CSRF)。
| 方式 | 请求头示例 |
|---|---|
| Bearer | Authorization: Bearer YOUR_KEY |
| 自定义请求头 | X-Infr-Api-Key: YOUR_KEY |
行为:
- 服务器把收到的令牌转换成 哈希,并以 恒定时间(常量时间比较) 与
infr_api_key.key_hash(仅限启用的密钥)比对。明文密钥不会被保存。 - 每个密钥 每分钟 120 次请求 的速率限制。超出则返回
429 rate_limited以及Retry-After响应头。 - 每次调用都会更新
last_used_at·last_used_ip·use_count。
主要端点:
# 连通性/密钥检查
curl -sS https://infraguard.example.com/api/v1/ping \
-H "Authorization: Bearer YOUR_KEY"
# 自动登记事件(已去重 · source=pulse)
curl -sS -X POST https://infraguard.example.com/api/v1/incidents \
-H "Authorization: Bearer YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"title":"NPE in checkout","fingerprint":"abc123","severity":"crit"}'
在 管理 ▸ API 密钥 中签发、吊销密钥并复制对接代码(明文密钥 只在签发时显示一次)。
Step-up — 保护破坏性操作
封禁、解封、恢复或修改设置等有风险的操作受到保护 —— 并非通过重新输入密码,而是通过三道防线:
- 级别复核 — 写入操作至少需要写入级别(2 以上),管理操作需要管理员(10),并在服务端重新校验。
- 审计日志 — 变更前后的值以及操作者和 IP 会被写入 审计日志(
infr_audit_log)。 - 界面确认 — 界面上有一个确认对话框(step-up)拦在操作之前。
数据来源
| 对象 | 用途 |
|---|---|
infr_user |
运维人员账号。列:login_id · password · user_level · name · email · lang · is_close · role_id · team_id · last_log · avatar |
| 会话存储(Redis) | infr_user_id 等会话键(REDIS_PREFIX 为 infr_) |
infr_api_key |
接入 API 密钥的 哈希(key_hash) · is_active · use_count · last_used_at/ip |
infr_audit_log |
破坏性/安全操作的审计轨迹 |
infr_user.password列绝不会出现在日志、URL 或 Cookie 中。API 密钥的明文只在签发时显示一次,数据库只保存哈希。
访问权限
/login·/logout— 任何人都可访问(访客)。- 整个控制台 — 需要有效会话(
infr.auth)。没有会话时,页面请求会重定向到/login,AJAX 请求会收到401 unauthenticated。
URL 级别的门槛(登录后评估):
| 区域 | 中间件 | 最低级别 |
|---|---|---|
| 常规控制台(概览 · 基础设施 · 资产只读) | infr.auth |
仅需登录(1) |
/security · /backup |
infr.level:2 |
2 |
/admin |
infr.level:10 |
10 |
/horizon · /pulse |
infr.admin |
10 |
| 开发区 | infr.dev |
9 |
级别档位:demo=1 · staff=5 · admin=10。完整策略参见 访问级别。
排障与 FAQ
我登录了,却一直被弹回 /login。
会话 Cookie 没有被保存(域名与 SESSION_DOMAIN 不匹配,或者仅限 HTTPS 的 Cookie 通过 HTTP 下发),或者账号被设为 is_close=1(已停用)。请让管理员检查账号状态。
我看到「账号或密码不正确。」
login_id 拼错、密码不对,或者账号已停用(is_close=1)。出于安全考虑,提示不会说明具体是哪一种。
账号无法登录。 可能是密码不正确或账号已被停用。请管理员在 用户与团队 中重置密码。
我能登录,但打不开 /admin。
你的级别低于 10。/admin 由 infr.level:10 把守,只有管理员才能进入。同样,/security 和 /backup 需要 2 级以上。
AJAX 调用返回 401。
控制台会话已过期。刷新页面并重新登录。JSON 请求收到的是 {"error":"unauthenticated"} 401,而不是重定向。
管理员提升了我的级别,却没有生效。
InfrAuth 在每次请求时都会重新同步会话,因此 从下一次页面跳转起 生效 — 无需重新登录。刷新即可立即生效。
菜单/界面显示成了意料之外的语言。
显示语言取自 infr_user.lang(会话中的 infr_lang),并在每次请求时重新同步。可在 本地化 中修改。
API 返回 401 invalid_api_key / 429 rate_limited。
密钥被停用或拼错会得到 401;每分钟超过 120 次请求会得到 429(+ Retry-After)。请在 API 密钥 中检查密钥状态并放慢调用频率。
相关条目
- 访问级别 —
user_level↔min_req,demo/staff/admin 档位 - 角色与团队 — 角色作用域与团队归属
- 用户与团队管理 — 创建账号、重置密码、停用
- API 密钥 — 签发接入 API 密钥与对接代码
- 本地化 — 显示语言与语言回退
- 审计日志 — 追踪破坏性操作
