@weibaohui/user-management
用户管理:给 dsh web 加登录门禁,未登录访问弹登录/注册页,首个注册者自动成为管理员;管理员可管理用户/角色,带登录与访问审计。
安装
dsh plugin --profile web add @weibaohui/user-management
沙箱测试结果
| 阶段 | 结果 |
|---|---|
| 1 安装 | ✓ |
| 2 列装 | ✓ |
| 3 挂载 | ✓ |
| 4 启动 | — |
四阶段在隔离沙箱里跑:安装(有网)→ 列装 → 挂载 → 启动(后三步断网)。
README
@weibaohui/user-management


用户管理 + 登录门禁:自带一个 HTTPS 网关(默认 https://<本机IP>:19843)挡在 dsh web 前面——未登录访问任何页面自动跳到登录/注册页,API 与 WebSocket 一律 401;内置管理员/普通用户两级角色,管理员管所有用户(新增、删除、禁用、重置密码、角色调整、IP 封禁、重置两步验证)并查看登录记录、访问记录、操作日志,普通用户只能查看和修改自己的信息。支持 TOTP 两步验证(Google Authenticator / 1Password 等验证器 App 扫码绑定,密码之后再加一道动态码)与注册审批制(自助注册默认待管理员启用)。
两步验证演示(登录动态码 + 扫码绑定,亮色 / 暗色):
亮色主题 · 暗色主题
!totp-light · !totp-dark
核心功能
- HTTPS 登录网关:独立
node:https监听器(默认端口 19843)反代 loopback 的 dsh web——所有请求先过会话校验,未登录的页面访问 302 跳/login,API 请求、WebSocket 升级一律 401。登录页为插件自带的独立页面(登录 / 注册双 Tab),不依赖宿主前端,深浅色自适应 - 首个注册者即管理员:系统内没有任何账号时,第一个注册的账号自动成为管理员,此后注册的都是普通用户
- 注册审批制(默认):
autoActivate关闭时,自助注册的账号(首个管理员除外)落为禁用状态——注册页提示「账号需管理员审核启用后方可登录」,管理员在用户表点「启用」即完成审批;开关打开则注册即激活直接登录。审批动作零新界面,复用用户表的禁用/启用按钮与状态徽章 - 用户管理(仅管理员):用户列表(角色 / 状态 / 两步验证 / 创建时间 / 最后登录)、新增用户(可选管理员或普通用户角色)、删除用户、重置密码(生成随机临时密码,仅展示一次)、提升 / 降级角色、禁用 / 启用账号(禁用立即踢出会话并拒绝登录)、重置两步验证(用户丢手机的恢复通道);最后一个管理员不可删、不可降、不可禁
- TOTP 两步验证(RFC 6238,默认关闭,自行开启):设置页扫码(二维码)或手输密钥绑定验证器 App,输入一个实时动态码完成绑定;开启后登录 = 用户名 + 密码 + 6 位动态码(登录页动态码框常驻选填:未开启留空即登,开启的账号必须校验,填错行内提示);登录表单带「记住用户名」勾选项;动态码 30 秒一换、一次一用(±1 窗口容时钟漂移,用过的码立即作废);连续错 5 次锁定 60 秒防爆破(
loginFailLimit/lockoutSeconds可调;内存态,重启清零);关闭需输登录密码确认,管理员可对任意用户重置。零第三方依赖,兼容 Google Authenticator / 1Password / 微软验证器 / Aegis 等 - 侧栏用户身份:dsh 侧栏左上角显示当前用户头像 + 用户名(适配侧栏收缩),点击弹出用户菜单——修改密码(改密后其他会话全部登出)、退出登录
- 三本审计账:登录记录(登录 / 登录失败 / 登出 / 改密 / 注册 / 重置密码 / 角色变更 / 删除 / 禁用 / 封禁等)、访问记录(页面级访问)、操作日志(经过网关的每一次 API 调用与 WebSocket 连接,含方法 / 路径 / 响应状态 / 来源 IP),均支持按用户、类型、路径、状态过滤
- IP 封禁:封禁列表 + 日志 IP 列一键封禁——命中地址在会话校验之前就被 403 拒绝(登录页也看不到);回环地址与当前请求所用 IP 服务端拒绝封禁,防止把自己锁在门外
- HTTPS 证书引导:自动签发 100 年自签证书(SAN 覆盖本机全部 IP + 每个 IP 的
sslip.io/nip.io通配 DNS 别名 + localhost);设置页「HTTPS 证书」页一键下载 PEM/DER、核对指纹、复制 macOS / Windows / Linux 导入命令——导入信任链后浏览器不再弹证书警告 - 网关证书管理界面(v0.10+):设置页「网关」页签集中管理 HTTPS 证书与监听状态——实时展示运行阶段 / 监听地址 / 上游 / 每个 SNI 站点的证书来源(自动生成 / 文件路径 / 本页添加)、覆盖主机名、有效期与指纹;粘贴 PEM 证书对即可上线真域名证书(先「检测」校验配对与 SAN,再「保存并应用」,私钥只存服务器
certs/custom/、0600 权限、界面不回显);一键重新生成自签证书(改了 hosts 后重签,替代手动删文件重启)与重启网关 - 会话持久:7 天滑动过期(
sessionDays可调),dsh 重启不掉线;HttpOnly Cookie,密码 scrypt 加盐哈希;认证与数据层零第三方依赖——TOTP 为 node:crypto 手写实现(过 RFC 6238 官方测试向量)、二维码为内置 MIT 编码器,仅证书签发用selfsigned
安装
dsh plugin --profile web add @weibaohui/user-management -w
装完重启 dsh web 即生效。
使用
1. 装完重启后,通过网关访问:https://<本机IP>:19843(浏览器首次会提示自签证书警告,按下方「HTTPS 证书」引导导入信任链后消失)——首个注册的账号就是管理员
2. 管理入口:Web UI → 设置页 → 用户管理,八个页签:用户 / 登录记录 / 访问记录 / 操作日志 / IP 封禁 / HTTPS 证书 / 网关 / 两步验证
3. 管理员:用户表(新增用户 / 重置密码 / 角色调整 / 禁用启用 / 重置两步验证 / 删除)+ 四本审计/封禁台账 + 证书下载与信任引导 + 网关状态与证书管理 + 自己的两步验证
4. 普通用户:个人信息卡 + 修改密码 + 两步验证 + 自己的登录记录
5. 开启两步验证:设置页「两步验证」页签(或个人信息卡)→ 开启 → 验证器 App 扫二维码(或手动输入密钥)→ 输入 App 显示的 6 位动态码 → 绑定完成;之后登录输用户名密码,在「两步验证码」框填入 App 现查的 6 位动态码(未开启的账号留空即可,不会报错);勾选「记住用户名」后下次登录自动带出用户名,密码交给浏览器密码管理器记忆
6. 侧栏左上角点头像/用户名:修改密码、退出登录
7. 数据与审计文件都在 ~/.dsh/user-management/(users.json / sessions.json / activity.jsonl / audit.jsonl / bans.json,0600 权限,原子写;审计账本滚动保留最近 5000 条)
8. IP 封禁按连接源地址(remoteAddress)判定:本插件自带的网关不做代理改写,看到的即是客户端真实地址;若你在网关前面另加反代层,看到的将是反代的 IP
给其他插件:解析请求的用户身份
user-management 向宿主提供 cordis 服务 user-management,兄弟插件据此知道自己收到的请求是"谁"(归属定时任务/分享/编辑记录等场景)。
消费方式(host 半)
// ⚠️ 不要把 'user-management' 写进静态 inject 数组 —— user-management 未安装时
// 你的插件会卡死激活。用运行时 inject,装了才有、没装就跳过:
module.exports = {
name: 'my-plugin',
inject: ['webServer'],
apply(ctx) {
ctx.inject(['user-management'], (scope) => {
const um = scope['user-management']
ctx.effect(() => ctx.webServer.register({
kind: 'prefix', path: '/my-plugin/api',
handler: async (req, res) => {
const user = await um.resolveRequest(req) // 解析请求 cookie
if (!user) return sendJson(res, 401, { error: '未登录' })
if (user.role !== 'admin') return sendJson(res, 403, { error: '需要管理员' })
// user.username / user.id 可用于操作归属
},
}), 'my-plugin: api')
})
},
}
API
方法 · 说明
resolveRequest(req) · 从请求的 um_session cookie 解析当前用户(推荐入口)
resolveToken(token) · 已自行取出 token 时的底层变体
返回 UmUser:{ id, username, role: 'admin'|'user', disabled, totpEnabled, createdAt, lastLoginAt };以下情况一律返回 null:未登录、会话过期/伪造、用户被禁用或删除(禁用/删除即刻失效)、user-management 自身存储故障(不向消费方抛错)。服务一 provide 即可安全调用(内部等待存储就绪)。
浏览器端(client 半)不需要这个服务
页面里直接 fetch('/user-management/api/session') 即可,网关本地应答 { user: {...} | null }(HttpOnly cookie 自动携带)。
边界
这只回答"这个请求是谁发的",不构成数据隔离——dsh 宿主的会话与工作区仍是全实例共享。另外身份可信的前提是 dsh web 只监听 loopback(本插件网关架构的默认要求):一旦有人绕过网关直连宿主端口,cookie 校验仍由 store 把关,但请保持宿主不对外暴露。
Remote Gateway 配置
v0.4 起,本插件自带 HTTPS 远程访问网关:dsh web 留在 loopback(127.0.0.1:3080),网关(独立 node:https 监听器)反代到它——网关是唯一对外入口,认证不可绕过。配置走 ~/.dsh/settings.yaml 的 user-management: 段(也支持设置页热生效)。
字段
字段 · 默认 · 说明
enabled · true · 关掉则不启动网关监听器
autoActivate · false · 注册审批开关。false(默认,审批制):自助注册的账号(首个管理员除外)直接落为禁用状态,注册页提示等待管理员审核,管理员在用户表点「启用」即完成审批;true:注册即激活、直接登录(历史行为)
listenHost · 0.0.0.0 · 网关监听地址;127.0.0.1 仅本机可达
port · 19843 · 网关 HTTPS 端口
sites[] · [](=自动) · 站点白名单 + 证书;空 = 自动枚举本机所有 IP(含 sslip.io / nip.io 别名)。配了 sites 与自动列表合并而非替换:见下方「场景 3」
sites[].hosts · — · Host 白名单(域名/IP,支持 *.example.com 通配)。不带 cert/key 的 site 的 hosts 合并进自签 site(去重)
sites[].cert / sites[].key · '' · PEM 文件路径(fullchain + privkey);**配了则保留为独
…(truncated)
以上内容来自 npm 上的 README,我们只做格式化,不改内容。
版本
npm 上共发布:28,最后更新 2026-10-04