安全体系
Zenith Admin 内置了多层安全防护能力,涵盖 IP 访问控制、账号锁定、密码策略、验证码及注册开关,均可通过系统配置页面在运行时动态调整。
IP 访问控制
通过 ipAccessMiddleware 对所有 /api/* 请求进行 IP 过滤,支持白名单与黑名单两种模式(可同时启用,黑名单优先执行)。
配置项
在后台「系统设置 → IP 访问控制」页面配置(对应 system_configs 表中的以下 key):
| 配置 Key | 类型 | 说明 |
|---|---|---|
ip_whitelist_enabled | boolean | 是否启用白名单。启用后只有名单内的 IP 可访问 |
ip_whitelist | string (JSON 数组) | 白名单 IP 列表,如 ["192.168.1.0/24", "10.0.0.1"] |
ip_blacklist_enabled | boolean | 是否启用黑名单。启用后名单内 IP 访问将收到 403 |
ip_blacklist | string (JSON 数组) | 黑名单 IP 列表,支持单 IP 与 CIDR 网段 |
工作机制
请求进入 /api/*
│
├── 免检路径(直接放行):
│ /api/auth/login、/api/auth/captcha、/api/auth/register
│ /api/auth/refresh、/api/oauth/*
│
├── 两者均未启用 → 直接放行
│
├── 黑名单已启用 → 命中则 403
│
└── 白名单已启用 → 未命中则 403- IP 来源优先从
X-Forwarded-For请求头中读取(取第一个值),其次读取X-Real-IP - 支持 CIDR 网段匹配(如
192.168.1.0/24),基于ip-range-check库实现 - 配置缓存 30 秒,修改后台配置后最多延迟 30 秒生效
Nginx 反代注意:确保 Nginx 已正确设置
X-Real-IP或X-Forwarded-For,否则后端收到的将是内网 IP。
账号锁定
连续登录失败超过阈值后,账号会被自动锁定一段时间,有效防止暴力破解。
相关配置项
| 配置 Key | 类型 | 默认值 | 说明 |
|---|---|---|---|
login_max_attempts | number | 10 | 最大失败次数,超过后自动锁定 |
login_lock_duration_minutes | number | 30 | 锁定持续时长(分钟) |
工作机制
- 失败计数以
loginAttempts:{username}为 key 存储于 Redis,服务重启后不重置 - 锁定到期后自动解除
- 管理员可在「用户管理」列表中点击「解除锁定」按钮,提前解除指定账号的锁定状态(调用
POST /api/users/:id/unlock)
密码策略
从 v0.1.4 起,支持通过系统配置控制密码复杂度要求与过期策略。
复杂度配置项
| 配置 Key | 类型 | 默认值 | 说明 |
|---|---|---|---|
password_min_length | number | 6 | 密码最小长度 |
password_require_uppercase | boolean | false | 是否必须包含大写字母 |
password_require_special_char | boolean | false | 是否必须包含特殊字符(!@#$%^&* 等) |
密码复杂度在以下场景触发校验:
- 用户创建(管理员操作)
- 用户修改个人密码
- 重置密码
密码过期
| 配置 Key | 类型 | 默认值 | 说明 |
|---|---|---|---|
password_expiry_enabled | boolean | false | 是否开启密码过期强制重置。开启后,当密码超期未更新,登录时将强制跳转修改密码页 |
password_expiry_days | number | 90 | 密码有效期天数,仅在 password_expiry_enabled 为 true 时生效 |
过期流程:
- 用户登录时,若
password_expiry_enabled = true,后端计算passwordUpdatedAt + password_expiry_days是否早于当前时间 - 若已过期,登录接口不返回 token,而是返回特殊 code(
password_expired)和临时 token - 前端检测到特殊 code 后,弹出「强制修改密码」弹窗
- 用户通过临时 token 完成密码修改后,方可正常使用系统
登录验证码
| 配置 Key | 类型 | 默认值 |
|---|---|---|
captcha_enabled | boolean | false |
启用后,登录页自动显示图形验证码输入框。验证码通过 GET /api/auth/captcha 获取(返回 Base64 图片 + captchaId),登录时需同时提交 captchaId 和用户输入的验证码文本,后端校验后自动失效。
注册开关
| 配置 Key | 类型 | 默认值 |
|---|---|---|
allow_registration | boolean | false |
false:登录页不显示「注册」入口,POST /api/auth/register返回 403true:开放注册,登录页显示「注册账号」链接
生产建议:如非公开注册场景,建议保持
allow_registration = false。
安全相关接口速查
| 接口 | 说明 |
|---|---|
GET /api/auth/captcha | 获取验证码(返回 Base64 图片 + captchaId) |
POST /api/auth/register | 开放注册(受 allow_registration 控制) |
POST /api/users/{id}/unlock | 管理员解除账号锁定 |
PUT /api/auth/password | 当前用户修改密码 |
POST /api/auth/forgot-password | 发送找回密码邮件 |
POST /api/auth/reset-password | 使用重置 token 设置新密码 |
PUT /api/users/{id}/password | 管理员修改指定用户密码 |
CSRF 防护
基于 hono/csrf 中间件校验请求的 Origin 头,防止第三方网站伪造表单或 AJAX 请求。
配置
通过环境变量 ALLOWED_ORIGINS 配置允许的来源白名单(在 .env 中设置):
# 留空 = 开发模式,不限制来源
ALLOWED_ORIGINS=
# 生产环境示例(逗号分隔)
ALLOWED_ORIGINS=https://admin.example.com,https://app.example.com放行规则
- 请求无
Origin头(服务端调用、Postman、curl)→ ✅ 直接放行 ALLOWED_ORIGINS为空(开发模式)→ ✅ 直接放行Origin在白名单中 → ✅ 放行Origin不在白名单中 → ❌ 403 Forbidden
生产建议:务必配置
ALLOWED_ORIGINS,否则任何来源的请求均可通过 CSRF 检查。
接口限流
基于 hono-rate-limiter + Redis 对高危接口进行限流,防止暴力破解和滥用。
当前限流策略
POST /api/auth/login:15 分钟内最多 10 次,用于防暴力破解密码GET /api/auth/captcha:1 分钟内最多 30 次,用于防验证码刷取POST /api/auth/register:1 小时内最多 5 次,用于防滥用注册POST /api/auth/forgot-password:1 小时内最多 5 次,用于防账号枚举POST /api/auth/reset-password:1 小时内最多 5 次,用于防重置密码滥用
超过限制时返回:
{
"code": 429,
"message": "操作过于频繁,请稍后再试",
"data": null
}实现细节
- 限流计数以 IP 地址为 key(
X-Forwarded-For优先,其次X-Real-IP,fallback0.0.0.0) - 计数器存储在 Redis(key 格式:
{prefix}rl:{ip}),服务重启后持续计数 - 实现位置:
packages/server/src/middleware/rate-limit.ts
反代注意:确保 Nginx 正确透传
X-Forwarded-For或X-Real-IP,否则所有请求将共享同一计数(fallback0.0.0.0),导致正常请求被误限。