外观
一文搞懂VitePress企业文档站权限分级与内网嵌入全流程
问题背景:企业内部文档站的核心痛点
当前大量技术团队选择VitePress搭建对外开源文档,但是直接复用这套架构搭建企业内部技术文档站时,普遍存在两个核心痛点:第一是原生VitePress没有内置权限管控能力,不同角色员工(开发、测试、产品、外包)的文档访问范围无法做差异化区分,敏感的架构设计文档、运维操作手册容易被外包人员随意查看;第二是内网资源嵌入困难,很多内部文档需要关联内网运维面板、CI/CD流水线、内部流程图等资源,直接暴露内网IP会泄露企业网络拓扑,存在安全隐患。 传统方案要么直接全量打包所有页面后靠网关做路由拦截,导致文档站构建体积随页面数量线性膨胀,上万页文档的构建时间超过15分钟,要么直接选用Confluence等付费文档系统,无法和团队现有Vue3技术栈打通定制化能力差。本文基于VitePress的扩展能力实现轻量权限分级与内网资源嵌入,兼顾构建性能和访问安全性。
方案对比:3种常见搭建方案优劣势分析
我们对比了行业内3种主流的企业内部文档站搭建方案,核心差异如下:
- 全量静态打包+网关拦截方案:所有文档页面全部预构建生成静态文件,由后端网关做路由鉴权,优势是无需修改VitePress原生逻辑,劣势是构建体积大、构建速度慢,敏感页面的静态文件依然存储在服务器上,一旦网关规则配置失误就会出现内容泄露。
- 第三方付费文档系统方案:选用Confluence、语雀等商用文档服务,优势是开箱即用自带权限体系,劣势是定制化能力差,无法嵌入团队自研的监控面板、内部运维工具,数据全部存储在第三方平台存在合规风险。
- 本文采用的动态路由+轻量权限校验方案:构建阶段仅打包公开通用文档,敏感文档运行时动态拉取,前端路由守卫做第一层权限校验,服务端接口做二次鉴权,兼顾构建性能和安全能力,完全兼容VitePress原生的Markdown语法、主题扩展能力。
实操步骤与可运行代码示例
1. 扩展VitePress自定义主题注入权限校验逻辑
首先我们基于VitePress的主题扩展能力,在应用初始化阶段注入全局路由守卫,所有路由跳转前先做权限校验,不需要修改VitePress的内核源码:
typescript
// .vitepress/theme/enhanceApp.ts
import type { EnhanceAppContext } from 'vitepress'
import DefaultTheme from 'vitepress/theme'
export default {
...DefaultTheme,
async enhanceApp({ app, router }) {
// 注册全局路由前置守卫
router.beforeEach(async (to, from, next) => {
// 公开白名单路径直接放行 无需鉴权
const publicPaths = ['/public/', '/login', '/403']
if (publicPaths.some(path => to.path.startsWith(path))) return true
// 调用企业统一SSO鉴权接口 获取当前用户角色与可访问路径
try {
const res = await fetch('/api/auth/user-perm', {
credentials: 'include'
})
const userPermInfo = await res.json()
// 未登录用户直接跳转登录页
if (!userPermInfo.isLogin) return '/login'
// 校验当前路径是否在用户可访问列表内
if (userPermInfo.accessPaths.includes(to.path)) return true
// 无权限跳转403提示页
return '/403'
} catch (e) {
// 接口请求失败直接跳转登录页 避免异常绕过鉴权
return '/login'
}
})
}
}2. 基于角色的分级路由动态过滤实现
在VitePress的配置文件中,我们可以基于环境变量区分公开页面和敏感页面,构建阶段仅打包公开页面,敏感页面运行时动态拉取Markdown内容渲染,避免敏感内容被预构建泄露:
typescript
// .vitepress/config.ts
import { defineConfig } from 'vitepress'
export default defineConfig({
themeConfig: {
nav: [
// 普通用户可见的公开导航
{ text: '首页', link: '/' },
{ text: '开发规范', link: '/public/dev-spec/' }
]
},
vite: {
build: {
rollupOptions: {
// 敏感文档路径排除在预构建列表外 运行时动态加载
external: (id) => id.startsWith('./docs/secret/')
}
}
}
})我们可以在导航栏组件中根据当前用户的角色权限动态渲染导航菜单,不同角色的用户看到的导航菜单完全不同,避免无关内容干扰正常访问。
3. 内网资源安全嵌入无泄露方案
通过Vite代理+Nginx反向代理的方式统一转发内网资源请求,前端完全不接触真实内网IP地址,避免内网拓扑泄露,开发阶段的代理配置如下:
typescript
// vite.config.ts 开发环境内网资源代理配置
import { defineConfig } from 'vite'
export default defineConfig({
server: {
proxy: {
// 所有内网资源请求统一走代理前缀 不暴露真实服务地址
'/internal/': {
target: 'http://192.168.1.200:9000/',
changeOrigin: true,
rewrite: path => path.replace(/^\/internal\//, '')
}
}
}
})生产环境中在Nginx中配置同样的代理规则,前端页面中直接通过/internal/monitor/dashboard这类相对路径就可以嵌入内网监控面板的iframe,完全不需要写真实内网地址。
避坑总结:4条来自生产实践的注意事项
- 禁止在前端静态资源中硬编码权限列表或敏感路径:很多开发者图方便把所有角色的可访问路径写在客户端JS里,导致打包后的JS文件可以直接被下载爬取,泄露未公开的内部文档路径,正确做法是所有权限数据都从内网鉴权接口动态拉取,接口返回后仅保存在内存中,页面刷新后自动销毁。
- 不要直接使用VitePress默认的静态404页做未授权访问兜底:原生VitePress的404页会遍历所有预生成的静态路由路径,容易被爬虫枚举所有文档路径,正确做法是在路由守卫中提前拦截未授权请求,直接跳转到统一403页面,不触发默认404逻辑。
- 内网资源嵌入必须配置服务端白名单校验:直接在前端写内网服务的真实IP地址很容易被抓包泄露企业内部网络拓扑,必须通过Vite开发代理+生产环境Nginx反向代理的方式统一转发内网资源请求,同时在Nginx配置中添加X-Frame-Options为文档站域名白名单,防止点击劫持攻击。
- 动态导入敏感页面不要开启预加载:VitePress默认会对所有Markdown页面做资源预加载,容易导致敏感页面的内容被提前缓存到浏览器本地,需要在Vite配置中把敏感路径的页面排除在预加载列表外,运行时再动态拉取Markdown内容渲染。 按照这套方案搭建的企业内部文档站,构建速度比传统全量打包方案提升70%以上,完全兼容VitePress的所有原生特性,同时满足内网文档的权限管控和资源嵌入需求。
免责声明
本工具与文章内容仅用于学习测试场景,优化建议仅供参考,请勿直接用于生产环境,由此产生的风险自行承担。