Files
jiahong 6333cf1091 更新
2026-03-25 22:52:11 +08:00

29 KiB
Raw Permalink Blame History

MkDocs 用户阅读管理系统 — 规划设计及实施方案

一、概述

1.1 目标

针对以 MkDocs 生成的静态文档站点(如 docs.writech.cn),构建一套用户阅读管理系统,实现用户登记与身份识别、文档分级权限控制、阅读次数与有效期管理、防爬虫保护和阅读行为统计分析。系统在保障文档安全可控的同时,为大部分公开文档提供免登录流畅阅读体验,仅在访问受控文档时才触发轻量认证。

1.2 与现有平台的关系

本系统基于 ICT 服务平台 已有基础设施构建,复用以下底座能力:

已有组件 本系统中的角色
KeycloakSSO 登录用户的 OIDC 认证中心,对接微信开放平台 Social IdP
OpenLDAP(身份源) 企业员工账户存储
DocForge(渲染引擎) MkDocs 构建触发与权限元数据解析
Umami(统计分析) 页面级阅读埋点与看板
PostgreSQL(数据库) 用户注册信息、权限策略、阅读记录持久化
Nginx(反向代理) 请求拦截、静态资源分发、SSL 终止

1.3 设计原则

  1. 静态站点不改造:MkDocs 仍按标准流程生成静态 HTML,用户系统以反向代理网关层注入,不侵入 MkDocs 构建产物
  2. 最小摩擦登录:公开文档免登录访问;受控文档首次需认证后自动记住,不逐页弹登录框
  3. 双通道注册:支持微信扫码和手机短信验证码两种轻量注册方式,适配 PC 和移动端
  4. 颗粒度可粗可细:权限可设置到单篇文档,也可按目录/标签/仓库整体授权
  5. 全栈开源:核心组件均采用开源方案,数据自托管

二、系统架构

2.1 整体架构图

@startuml
skinparam componentStyle rectangle
skinparam nodesep 10
skinparam ranksep 25
title MkDocs 用户阅读管理系统 - 整体架构

actor "PC 浏览器" as PCUser
actor "手机浏览器" as MobileUser

rectangle "互联网应用服务器" {

  package "接入层" as P_Access {
    [Nginx\n反向代理] as Nginx
    [MkDocs Guard\n认证网关] as Guard
  }

  package "认证服务" as P_Auth {
    [Keycloak SSO] as KC
    [微信扫码服务] as WxAuth
    [短信验证服务] as SMS
  }

  package "用户管理" as P_User {
    [用户注册中心] as UC
    [权限引擎] as Perm
    [反爬虫模块] as AntiBot
  }

  package "文档服务" as P_Doc {
    [MkDocs\n静态站点] as MkDocs
    [DocForge\n渲染引擎] as Forge
  }

  package "数据与分析" as P_Data {
    database "PostgreSQL" as DB
    [Redis\n会话/限流] as Redis
    [Umami 统计] as Umami
  }
}

' === 分层布局控制 ===
P_Access -[hidden]down-> P_Auth
P_Access -[hidden]down-> P_User
P_Auth -[hidden]right-> P_User
P_Auth -[hidden]down-> P_Doc
P_User -[hidden]down-> P_Data
P_Doc -[hidden]right-> P_Data

' === 用户入口 ===
PCUser -down-> Nginx
MobileUser -down-> Nginx
Nginx -down-> Guard : 文档请求

' === Guard 请求分发 ===
Guard -down-> KC : OIDC 认证
Guard -down-> Perm : 查询权限
Guard -down-> AntiBot : 请求检测
Guard -down-> MkDocs : 放行静态页面

' === 注册中心 ===
UC --> WxAuth : 微信登录
UC --> SMS : 短信验证

' === 数据持久化 ===
UC -down-> DB : 用户数据
Perm -down-> DB : 权限策略
Perm -down-> Redis : 缓存
Forge -down-> DB : 文档元数据
Umami -down-> DB : 阅读埋点
@enduml

2.2 核心组件清单

组件 技术方案 用途 许可证
认证网关 MkDocs Guard(自研,Go / Node.js Nginx 子请求认证,拦截文档请求并校验权限
用户注册中心 UserCenter(自研,Node.js + Express 微信扫码、短信验证码注册/登录,用户管理
权限引擎 PermEngine(自研,Node.js 文档 - 用户权限匹配、次数/期限校验
反爬虫 AntiBot(自研 + 开源中间件) 频率限制、浏览器指纹、行为检测
微信 OAuth 微信开放平台 + Keycloak Social IdP 微信扫码登录/注册
短信网关 阿里云短信服务 / 腾讯云短信 验证码发送
会话缓存 Redis 登录会话、验证码、频率计数器 BSD
浏览器指纹 FingerprintJS(开源版) 匿名用户标识、反爬辅助 MIT
静态站点 MkDocs + Material 主题 Markdown 文档构建为静态 HTML BSD
统计分析 Umami(已有) 页面访问量、阅读时长、用户来源 MIT
数据库 PostgreSQL(已有) 用户、权限、阅读记录持久化 PostgreSQL

2.3 请求处理流程

@startuml
skinparam componentStyle rectangle
title 文档请求处理流程

start
:用户请求 MkDocs 文档页面;

:Nginx 转发至 MkDocs Guard;

:AntiBot 检测;
if (疑似爬虫 ?) then (是)
  :返回 429 / 验证码挑战;
  stop
else (否)
endif

:Guard 查询文档权限级别;

if (文档为 open 级别 ?) then (是)
  :直接放行, 返回静态页面;
  :Umami 记录匿名访问;
  stop
else (否)
endif

if (用户已持有有效会话 ?) then (是)
  :从 Redis 读取用户身份;
else (否)
  :展示登录浮层;
  note right
    PC - 微信扫码 / 短信验证码
    手机 - 微信授权 / 短信验证码
  end note
  :完成认证, 建立会话;
endif

:PermEngine 校验用户权限;
if (用户有权访问 ?) then (是)
  if (阅读次数/有效期已超限 ?) then (是)
    :提示 "使用权已到期, 请联系管理员";
    stop
  else (否)
    :放行, 返回静态页面;
    :记录阅读日志;
    :Umami 记录实名访问;
    stop
  endif
else (否)
  :返回 403 无权限页面;
  stop
endif

@enduml

三、用户注册与登录

3.1 注册方式

系统支持两种轻量注册方式,用户无需设置密码:

3.1.1 微信扫码注册/登录

@startuml
skinparam componentStyle rectangle
title 微信扫码注册/登录流程

start
:用户在 PC 浏览器访问受控文档;
:Guard 弹出登录浮层;
:浮层展示微信登录二维码;
note right: 调用微信开放平台生成临时二维码

:用户使用微信扫码;
:微信弹出授权确认;
:用户点击确认授权;

:微信回调 Keycloak 携带 code;
:Keycloak 换取微信 OpenID + 用户信息;

if (OpenID 已绑定系统用户 ?) then (是)
  :直接登录, 建立会话;
else (否)
  :自动创建用户;
  note right
    昵称 - 微信昵称
    头像 - 微信头像
    标识 - 微信 OpenID
    角色 - 默认读者
  end note
  :登录并建立会话;
endif

:回到文档页面, 自动刷新;
stop
@enduml

技术实现要点

  • Keycloak 配置微信开放平台作为 Social Identity Provider
  • 使用微信开放平台「网站应用」的扫码登录能力(非公众号)
  • PC 端展示二维码供手机扫描;手机端直接调起微信授权页面
  • 微信 OpenID 与系统用户 1:1 绑定,存储于 PostgreSQL

3.1.2 手机短信验证码注册/登录

@startuml
skinparam componentStyle rectangle
title 短信验证码注册/登录流程

start
:用户在浮层选择 "手机号登录";
:输入手机号;

:UserCenter 检查发送频率;
if (60 秒内已发送 ?) then (是)
  :提示 "请稍后重试";
  stop
else (否)
endif

:生成 6 位随机验证码;
:验证码存入 Redis (有效期 5 分钟);
:调用短信网关发送;

:用户输入验证码;

if (验证码正确 ?) then (是)
  if (手机号已注册 ?) then (是)
    :直接登录, 建立会话;
  else (否)
    :自动创建用户;
    note right
      标识 - 手机号
      角色 - 默认读者
    end note
    :登录并建立会话;
  endif
else (否)
  :提示 "验证码错误";
  stop
endif

:回到文档页面;
stop
@enduml

技术实现要点

配置项
验证码长度 6 位纯数字
有效期 5 分钟
发送间隔 60 秒
单手机号日上限 10 次
短信模板 「您的 Writech 文档验证码为 {code},5 分钟内有效。」
频率计数器 Redis INCR + TTL

3.2 PC 浏览器认证体验

PC 端用户访问受控文档时,页面内弹出半透明遮罩 + 居中登录浮层(非跳转),浮层包含两个标签页:

标签页 内容
微信扫码 显示动态二维码,扫码后自动完成登录并关闭浮层
短信验证 手机号输入框 + 发送按钮 + 验证码输入框

浮层设计原则:

  • 背景半透明可窥见文档内容(制造阅读意愿)
  • 登录成功后浮层自动消失,无页面跳转
  • 会话有效期 7 天,有效期内不再弹出

3.3 手机浏览器认证体验

  • 微信内打开:直接调起微信授权(静默授权 + 头像/昵称),无需扫码
  • 其他浏览器:展示短信验证码登录界面,微信扫码二维码作为备选

3.4 会话管理

参数 说明
会话存储 Redis key = session:{token}value = 用户信息 JSON
会话有效期 7 天 自最后一次请求起滑动续期
会话 Token HttpOnly Cookie mkdocs_sessionSecure + SameSite=Lax
并发会话 不限 同一用户可在多设备同时登录

四、用户管理

4.1 用户数据模型

users
├── id              UUID        主键
├── phone           VARCHAR(20) 手机号(唯一,可为空)
├── wechat_openid   VARCHAR(64) 微信 OpenID(唯一,可为空)
├── wechat_unionid  VARCHAR(64) 微信 UnionID(可为空)
├── nickname        VARCHAR(50) 昵称
├── avatar_url      TEXT        头像 URL
├── role            ENUM        角色:reader / reviewer / blocked
├── list_type       ENUM        名单类型:normal / whitelist / blacklist
├── group_id        UUID        所属用户组(外键)
├── registered_at   TIMESTAMP   注册时间
├── last_login_at   TIMESTAMP   最后登录时间
└── status          ENUM        状态:active / suspended / deleted

4.2 用户角色

角色 标识 权限说明
读者 reader 默认角色,可阅读被授权的文档,可提交反馈
审阅者 reviewer 受邀审阅指定文档,可阅读、评论并提交修改建议
被封禁 blocked 黑名单用户,禁止访问所有受控文档
管理员 admin 管理用户、权限策略、查看统计(复用 Keycloak 管理员角色)

说明:「作者」角色由 Gitea 仓库写权限定义,不在本系统管理范围内。

4.3 白名单与黑名单

白名单机制

  • 白名单用户可访问所有 login 级别文档,无需逐文档授权
  • 适用场景:VIP 客户、长期合作伙伴、内部测试人员
  • 管理方式:管理员在后台设置 list_type = whitelist

黑名单机制

  • 黑名单用户访问任何受控文档均返回 403
  • 触发条件:
    • 管理员手动加入
    • AntiBot 检测到恶意爬取行为自动加入
    • 用户连续提交违规反馈内容
  • 管理方式:管理员在后台设置 list_type = blacklist

名单优先级

黑名单 > 文档级 restricted > 白名单 > 文档级 login > 普通用户

4.4 用户分组

用户可归入多个组,方便按组授权:

user_groups
├── id          UUID        主键
├── name        VARCHAR(50) 组名(如"经销商"、"教育局客户"
├── description TEXT        说明
└── created_at  TIMESTAMP

user_group_members
├── user_id     UUID        外键 → users
├── group_id    UUID        外键 → user_groups
└── joined_at   TIMESTAMP

授权时可指定用户组,该组全部成员自动获得对应权限。


五、文档权限模型

5.1 权限分级

在现有 DocPortal 权限体系基础上,MkDocs 用户系统使用以下五级访问控制

级别 标识 说明 是否需要登录
完全公开 open 任何人可无限次阅读,不触发任何认证
试读公开 open_once 匿名用户首次可阅读,再次访问需登录 首次否,再次是
登录可读 login 登录用户(含白名单)可阅读
指定可读 restricted 仅授权用户/用户组可阅读
内部机密 confidential 仅管理员和显式授权用户,额外审计日志 是 + 审计

5.2 文档操作权限

每篇文档可独立配置以下操作权限:

权限 标识 说明
只读 read 仅可在线阅读,不可复制/下载
反馈 feedback 可提交反馈意见(文字/评分),不可修改文档
评论 comment 可在文档段落级别发表评论
下载 download 可下载 Markdown 原文或 PDF
修改建议 suggest 可提交修改建议(生成 Git PR),需作者审核

5.3 权限配置方式

方式一:Front Matter 声明(文档级)

在 Markdown 文件头部的 YAML Front Matter 中声明权限:

---
title: "智能笔 BLE 通信协议"
access: restricted
permissions:
  read: true
  feedback: true
  comment: true
  download: false
  suggest: false
allowed_users:
  - "138****1234"
  - "group:经销商"
allowed_groups:
  - "经销商"
  - "教育局客户"
usage:
  max_views: 50
  expires_at: "2027-03-31"
---

方式二:目录级继承(.access.yml 配置文件)

在 MkDocs 目录下放置 .access.yml 文件,该目录及子目录下所有文档继承此权限配置:

# docs/技术方案/.access.yml
default_access: login
default_permissions:
  read: true
  feedback: true
  comment: false
  download: false
  suggest: false
overrides:
  - pattern: "公开简介.md"
    access: open
  - pattern: "机密/**"
    access: confidential

方式三:管理后台配置(全局策略)

管理员在 Web 后台按仓库、目录路径通配符、标签维度设置批量策略:

策略维度 示例 说明
仓库 com-shware-doc 整个仓库默认 login
目录通配符 */对外文档/** 所有仓库的「对外文档」目录 open
标签 tag:机密 含「机密」标签的文档 confidential
单文档 SmartPen/硬件设计/xxx.md 指定文档覆盖策略

5.4 权限优先级(从高到低)

1. 黑名单 → 403 拒绝(最高优先)
2. 单文档 Front Matter 声明
3. 管理后台单文档策略
4. 目录级 .access.yml
5. 管理后台目录通配符策略
6. 管理后台标签策略
7. 管理后台仓库默认策略
8. 系统默认(open)

5.5 避免登录繁杂的设计策略

为满足"颗粒度到单文档但不逐页登录"的要求,采用以下策略:

策略 机制
会话全站有效 登录一次后 7 天内访问同站点任何受控文档均免再次登录
目录级继承 同目录下文档共享权限配置,无需逐文件设置
公开文档零感知 open 级文档不注入任何认证逻辑,加载速度等同纯静态页
延迟认证 用户浏览目录/搜索时不触发登录,点击具体受控文档才弹浮层
渐进式体验 受控文档先展示标题+摘要(前 3 段),余下内容模糊化并提示登录

六、文档使用控制

6.1 阅读次数限制

通过 Front Matter 或管理后台配置 max_views 参数:

usage:
  max_views: 50         # 该文档最多可被在线阅读 50 次
  max_views_per_user: 5  # 每个用户最多阅读 5 次

计数规则

规则 说明
计数触发 用户打开文档页面并停留超过 5 秒才计为 1 次有效阅读
去重窗口 同一用户 30 分钟内多次打开同一文档仅计 1 次
存储位置 PostgreSQL doc_view_logs
缓存加速 Redis 缓存当前计数,每 10 分钟同步至数据库

6.2 有效期限制

usage:
  expires_at: "2027-03-31"    # 到期后文档不可访问
  available_from: "2026-06-01" # 生效日期(可选)

到期处理策略

场景 行为
到期后匿名访问 返回"文档已过期"提示页
到期后登录访问 返回"文档已过有效期,请联系管理员"提示页
管理员访问 不受限制,可正常查看
即将到期提醒 到期前 7 天向管理员发送 MsgHub 通知

6.3 使用控制数据表

doc_usage_config
├── doc_id          VARCHAR     文档唯一标识(仓库 + 路径)
├── max_views       INT         总阅读次数上限(NULL = 不限)
├── max_views_user  INT         单用户阅读次数上限(NULL = 不限)
├── available_from  DATE        生效日期(NULL = 即时生效)
├── expires_at      DATE        到期日期(NULL = 永不过期)
└── updated_at      TIMESTAMP

doc_view_logs
├── id              UUID        主键
├── doc_id          VARCHAR     文档标识
├── user_id         UUID        用户 ID(匿名用户为 NULL
├── fingerprint     VARCHAR     浏览器指纹(匿名用户标识)
├── ip_address      INET        访问 IP
├── user_agent      TEXT        浏览器 UA
├── duration_sec    INT         停留时长(秒)
├── viewed_at       TIMESTAMP   访问时间
└── is_counted      BOOLEAN     是否计入有效阅读

七、防爬虫策略

7.1 多层防护体系

@startuml
title 防爬虫多层防护体系

start
:外部请求;

partition "第一层 - Nginx 基础防护" {
  :IP 频率限制;
  :User-Agent 黑名单;
  :robots.txt 声明;
}

if (命中拦截?) then (是)
  :返回 429 / 403;
  stop
else (否)
endif

partition "第二层 - AntiBot 行为检测" {
  :浏览器指纹验证;
  :JS 执行检测;
  :鼠标/滚动行为分析;
}

if (疑似爬虫?) then (是)
  :验证码挑战;
  stop
else (否)
endif

partition "第三层 - 内容保护" {
  :关键内容异步加载;
  :文本防选择/防复制;
  :动态水印注入;
}

:返回受保护的文档内容;

partition "第四层 - 智能封禁(后台异步)" {
  :分析访问行为模式;
  if (触发封禁条件?) then (是)
    :自动加入黑名单;
  else (否)
  endif
}

stop
@enduml

7.2 各层策略详情

第一层:Nginx 基础防护

策略 配置 说明
IP 频率限制 limit_req_zone 10 次/秒 单 IP 超限返回 429
并发连接限制 limit_conn 20 连接/IP 超限返回 503
User-Agent 黑名单 拦截 curl/wget/scrapy/python-requests 等 返回 403
robots.txt 仅允许搜索引擎索引 open 级文档路径 受控文档 Disallow

第二层:AntiBot 行为检测

策略 实现 说明
JS 执行检测 页面注入一段 JS,执行后生成 Token 回传 无 JS 执行能力的爬虫无法通过
浏览器指纹 FingerprintJS 生成指纹,与 User-Agent 交叉验证 指纹缺失或矛盾判定为爬虫
行为分析 监测鼠标移动、页面滚动、停留时长 零行为数据判定为爬虫

第三层:内容保护

策略 实现 适用级别
关键内容异步加载 受控文档正文通过 AJAX 请求获取,HTML 源码不含全文 login / restricted / confidential
CSS 防选择 user-select: none + 右键菜单拦截 restricted / confidential
动态水印 用户昵称 + 手机号后 4 位半透明水印 confidential

第四层:智能封禁

触发条件 处置
同一 IP 10 分钟内访问 > 100 个不同文档 自动加入 IP 黑名单 24 小时
同一用户 1 小时内访问 > 50 个文档 账户临时冻结 + 管理员通知
被 JS 检测拦截 > 3 次 IP + 指纹 永久封禁

八、阅读统计

8.1 统计维度

基于 Umami 埋点和系统自有日志,提供以下统计维度:

维度 数据来源 说明
页面访问量 (PV) Umami 每篇文档的总访问次数
独立访客数 (UV) Umami + 指纹 去重后的独立用户数
平均阅读时长 Umami 用户在文档页面的平均停留时间
阅读完成率 前端 JS 埋点 滚动到文档底部的用户占比
用户阅读清单 doc_view_logs 每个登录用户阅读过的文档列表
文档热度排行 聚合计算 按 PV / UV 排序的文档热度榜
时段分布 Umami 按小时/日/周分布的访问趋势
来源渠道 Umami (Referrer) 文档从哪些渠道/页面被引流

8.2 管理看板

管理员可在 Web 后台查看以下看板:

看板 内容
文档总览 各级别文档数量、总 PV/UV、活跃用户数
文档详情 单篇文档的访问趋势、用户列表、阅读时长分布
用户画像 单用户阅读历史、偏好标签、活跃时段
权限使用 各文档的已用/剩余阅读次数、即将到期提醒
安全日志 爬虫拦截记录、黑名单触发记录、异常访问报警

8.3 自定义埋点

在 MkDocs 主题模板中注入以下自定义事件:

// 页面加载时
umami.track('doc_view', {
  doc_id: '{{ doc_id }}',
  access_level: '{{ access_level }}',
  user_type: '{{ user_type }}'  // anonymous / registered / whitelist
});

// 阅读完成时(滚动到底部)
window.addEventListener('scroll', function() {
  if (isScrolledToBottom()) {
    umami.track('doc_complete', { doc_id: '{{ doc_id }}' });
  }
});

// 反馈提交时
function onFeedbackSubmit(rating, content) {
  umami.track('doc_feedback', {
    doc_id: '{{ doc_id }}',
    rating: rating
  });
}

九、MkDocs Guard 技术实现

9.1 架构模式:Nginx auth_request

MkDocs Guard 采用 Nginx auth_request 子请求模式,不修改 MkDocs 静态文件:

# Nginx 配置片段
location /docs/ {
    auth_request /auth/check;
    auth_request_set $auth_user $upstream_http_x_auth_user;
    auth_request_set $auth_role $upstream_http_x_auth_role;

    # 认证通过后,代理到 MkDocs 静态文件
    proxy_pass http://127.0.0.1:8001;

    # 注入用户信息到响应头(供前端 JS 使用)
    add_header X-Auth-User $auth_user;
    add_header X-Auth-Role $auth_role;
}

location = /auth/check {
    internal;
    proxy_pass http://127.0.0.1:3100/guard/check;
    proxy_set_header X-Original-URI $request_uri;
    proxy_set_header X-Forwarded-For $remote_addr;
    proxy_set_header Cookie $http_cookie;
}

9.2 Guard 服务端逻辑

MkDocs Guard (Node.js, 端口 3100)
├── GET /guard/check          — Nginx auth_request 回调
│     ├─ 解析 X-Original-URI 获取文档路径
│     ├─ 查询文档权限级别(Redis 缓存 → PostgreSQL
│     ├─ open 级别 → 200 放行
│     ├─ 检查 Cookie 中的会话 Token
│     ├─ 有效会话 → 调用 PermEngine 校验 → 200/403
│     └─ 无会话 + 非 open → 401(触发前端弹登录浮层)
├── POST /guard/login/wechat  — 微信扫码登录回调
├── POST /guard/login/sms     — 短信验证码登录
├── POST /guard/logout        — 登出
└── GET  /guard/session       — 查询当前会话状态

9.3 PermEngine 权限引擎

PermEngine (Node.js 模块)
├── checkAccess(userId, docPath)
│     ├─ 检查用户黑名单 → blocked 则拒绝
│     ├─ 加载文档权限配置(优先级链)
│     ├─ open → 放行
│     ├─ open_once → 查浏览器指纹记录
│     ├─ login → 已登录即放行;白名单用户放行
│     ├─ restricted → 检查 allowed_users / allowed_groups
│     ├─ confidential → 检查显式授权 + 写审计日志
│     └─ 检查 usage 限制(次数 + 日期)
├── loadDocConfig(docPath)
│     ├─ 查 Redis 缓存
│     ├─ 缓存未命中 → 查 PostgreSQL
│     ├─ 合并优先级链(Front Matter > 单文档 > 目录 > 仓库)
│     └─ 写入 Redis 缓存(TTL 5 分钟)
└── getPermissions(userId, docPath)
      └─ 返回用户在该文档上的操作权限集合

9.4 数据库完整设计

@startuml
skinparam componentStyle rectangle
title 用户阅读管理系统 - 数据表关系

rectangle "users\n用户表" as Users {
  rectangle "id, phone, wechat_openid\nnickname, role, list_type\ngroup_id, status" as UF
}

rectangle "user_groups\n用户组表" as Groups {
  rectangle "id, name, description" as GF
}

rectangle "user_group_members\n组成员表" as Members {
  rectangle "user_id, group_id" as MF
}

rectangle "doc_access_policies\n文档权限策略表" as Policies {
  rectangle "id, doc_pattern, access_level\npermissions, allowed_users\nallowed_groups, priority" as PF
}

rectangle "doc_usage_config\n使用控制表" as Usage {
  rectangle "doc_id, max_views\nmax_views_user\navailable_from, expires_at" as UGF
}

rectangle "doc_view_logs\n阅读日志表" as Logs {
  rectangle "id, doc_id, user_id\nfingerprint, ip, duration\nviewed_at" as LF
}

rectangle "antibot_blocklist\n封禁记录表" as Block {
  rectangle "id, ip, fingerprint\nuser_id, reason\nblocked_until" as BF
}

Users -right-> Members
Groups -down-> Members
Users -down-> Logs
Policies -down-> Usage
@enduml

十、管理后台

10.1 功能模块

管理后台作为 MkDocs Guard 的 Web 管理界面,提供以下功能:

模块 功能
用户管理 查看用户列表、搜索、设置白名单/黑名单、编辑用户组
权限管理 配置文档访问策略、目录级/标签级批量授权、授权用户/组
使用控制 设置文档阅读次数上限、有效期、查看用量
阅读统计 文档热度排行、用户阅读画像、渠道来源分析
安全中心 爬虫拦截日志、封禁记录管理、异常告警配置
系统配置 短信网关配置、微信开放平台参数、会话策略调整

10.2 管理后台技术栈

组件 技术
前端框架 React + Ant Design
后端 API Node.js + Express(与 Guard 同进程)
认证 Keycloak SSOadmin 角色)
部署 与 MkDocs Guard 同容器,路由 /admin/

十一、部署方案

11.1 服务清单

服务 端口 部署位置 说明
Nginx 443 互联网应用服务器 SSL 终止 + auth_request
MkDocs 静态站 8001 互联网应用服务器 由 DocForge 自动构建
MkDocs Guard 3100 互联网应用服务器 认证网关 + 权限引擎 + 管理后台
Keycloak 8080 互联网应用服务器(已有) SSO 认证中心
Redis 6379 互联网应用服务器 会话 + 缓存 + 限流
PostgreSQL 5432 私有云(已有) 用户数据 + 权限策略 + 日志
Umami 3000 互联网应用服务器(已有) 统计分析

11.2 Docker Compose 部署

services:
  mkdocs-guard:
    image: node:20-alpine
    ports:
      - "3100:3100"
    environment:
      - DATABASE_URL=postgresql://guard:***@db:5432/mkdocs_guard
      - REDIS_URL=redis://redis:6379/2
      - KEYCLOAK_URL=https://sso.writech.cn
      - KEYCLOAK_REALM=writech
      - WECHAT_APP_ID=${WECHAT_APP_ID}
      - WECHAT_APP_SECRET=${WECHAT_APP_SECRET}
      - SMS_ACCESS_KEY=${SMS_ACCESS_KEY}
      - SMS_ACCESS_SECRET=${SMS_ACCESS_SECRET}
    depends_on:
      - redis

  mkdocs-site:
    image: nginx:alpine
    ports:
      - "8001:80"
    volumes:
      - ./site:/usr/share/nginx/html:ro

  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"
    volumes:
      - redis_data:/data

volumes:
  redis_data:

十二、实施路线图

阶段 时间 任务 交付物
一期:基础认证 第 1-3 周 MkDocs Guard + Nginx auth_request 集成;微信扫码登录;短信验证码登录;会话管理 用户可通过微信/短信登录访问受控文档
二期:权限引擎 第 4-6 周 PermEngine 开发;Front Matter 权限解析;目录级 .access.yml 继承;白名单/黑名单管理 文档可按级别和用户/组授权
三期:使用控制 第 7-8 周 阅读次数限制;有效期管理;渐进式体验(摘要预览 + 模糊化) 文档使用可按次数和时间控制
四期:防爬虫 第 9-10 周 AntiBot 四层防护部署;智能封禁规则;内容异步加载 爬虫有效拦截
五期:统计与后台 第 11-13 周 Umami 自定义埋点;管理后台开发;阅读统计看板;安全日志 管理员可视化管理全部功能
六期:优化上线 第 14-15 周 性能调优;安全审计;用户体验打磨;正式上线 系统全功能投入运营