Excalidraw LDAP用户目录对接实践
在企业协作工具日益普及的今天,如何在保障数据安全的前提下实现高效的团队协同,成为技术管理者面临的核心挑战之一。尤其对于重视数据主权与合规性的组织而言,直接使用公有云白板服务往往存在风险。Excalidraw 作为一款轻量、开源且支持私有化部署的虚拟白板工具,凭借其手绘风格和极简交互,正逐渐被引入内网环境用于架构设计、流程梳理等场景。
但问题也随之而来:没有登录机制的白板,真的能用吗?
答案是——可以,但必须补上身份认证这一环。否则,任何知道地址的人都能访问甚至修改关键图表,这显然无法满足企业级安全要求。幸运的是,大多数企业已有成熟的用户管理体系,比如基于 OpenLDAP 或 Active Directory 的统一身份目录。如果能让 Excalidraw “认得清谁是谁”,就能在不牺牲用户体验的前提下,实现安全可控的协作。
于是,一个自然的想法浮现出来:能不能像保护内部 Wiki 或监控系统一样,通过统一认证网关来拦截对 Excalidraw 的访问请求,并利用现有的 LDAP 目录完成身份校验?
这正是本文要解决的问题。我们不会去修改 Excalidraw 源码,也不会开发复杂的插件体系,而是采用一种更优雅的方式——借助反向代理与独立认证服务,在容器化环境中实现无侵入式的 LDAP 集成。
Excalidraw 本身是一个典型的前端主导型应用。它的核心逻辑运行在浏览器中,图形数据默认保存在localStorage里,后端仅负责 WebSocket 协同状态同步或可选的数据持久化。这种设计让它极其轻便,启动一个 Docker 容器即可对外提供服务:
docker run -p 80:80 excalidraw/excalidraw但也正因为如此,它原生不具备用户管理系统。你不能指望它自带“注册”“登录”“权限分组”这些功能。但这反而成了优势:正因为简单,才更容易被集成进复杂的企业环境。
真正的挑战在于,如何让这样一个“匿名即可用”的工具,变成只有特定员工才能访问的受控平台。如果我们强行给它加上账号系统,不仅破坏了其简洁性,还会增加维护成本,失去快速升级上游版本的能力。
所以思路必须转变:不是让 Excalidraw 去做认证,而是让整个访问链路先过一道门禁。
这就引出了我们的架构选择:在客户端和 Excalidraw 实例之间插入一层具备认证能力的反向代理,例如 Nginx 或 Traefik。当用户尝试打开白板页面时,代理会首先检查其是否已通过身份验证;如果没有,则跳转到登录页或拒绝访问;只有认证通过后,才会将请求放行至后端的 Excalidraw 容器。
而这个“认证动作”的具体执行者,并不由代理直接完成,而是交由一个独立的认证适配器微服务处理。该服务专门负责与 LDAP 服务器通信,完成 Bind(绑定)、Search(搜索)和权限判断。
为什么需要中间层?因为直接在 Nginx 中嵌入 LDAP 认证逻辑虽然可行(如使用auth_request+ 自定义脚本),但灵活性差、调试困难,且难以实现复杂的授权策略(比如“必须属于某个组”)。相比之下,用 Python 或 Go 编写一个轻量级认证服务,既能灵活处理各种边界情况,又能复用于其他系统。
来看一个典型的工作流:
- 用户访问
https://whiteboard.company.com - 反向代理检测到请求未携带有效会话 Cookie
- 将请求重定向至
/auth/ldap?return_url=/,或发起 Basic Auth 挑战 - 浏览器弹出登录框,用户输入域账号密码
- 代理将凭据转发至认证服务
/verify - 认证服务连接 LDAP 服务器,尝试以
uid=john,ou=Users,dc=company,dc=com身份 Bind - 若成功,进一步查询该用户是否在允许访问的组中(如
(cn=design-team)) - 权限符合则返回 HTTP 200,代理设置短期 Token Cookie 并放行原始请求
- 用户加载 Excalidraw 页面,开始绘图协作
整个过程对 Excalidraw 完全透明,它依然认为自己是“开放”的,只是背后的网络通道已经被严密管控。
为了支撑这套机制,我们需要几个关键组件协同工作:
- Excalidraw 容器:保持官方镜像不变,专注于提供静态资源和协同服务;
- 反向代理:推荐使用 Traefik(Kubernetes 场景)或 Nginx(传统部署),启用
auth_request或 ForwardAuth 支持; - 认证适配器:一个独立运行的 Web 服务,暴露
/auth和/verify接口; - LDAP 服务器:企业现有目录服务,支持明文或 LDAPS 连接;
- 会话管理:短期 JWT 或签名 Cookie,避免频繁调用 LDAP。
其中,认证适配器的设计尤为关键。以下是一个简化版的 Python 实现示例,使用ldap3库完成核心逻辑:
import ldap3 from flask import Flask, request, jsonify app = Flask(__name__) LDAP_SERVER = "ldaps://ldap.company.com" BASE_DN = "dc=company,dc=com" USER_OU = "ou=Users" GROUP_DN = "cn=excalidraw-access,ou=Groups,dc=company,dc=com" @app.route('/verify', methods=['POST']) def verify(): username = request.json.get('username') password = request.json.get('password') if not username or not password: return '', 401 user_dn = f"uid={username},{USER_OU},{BASE_DN}" server = ldap3.Server(LDAP_SERVER, connect_timeout=5) conn = ldap3.Connection(server, user=user_dn, password=password, auto_bind=False) try: # 尝试绑定(认证) if not conn.bind(): return '', 401 # 检查是否属于授权组 conn.search( search_base=GROUP_DN, search_filter='(memberUid={})'.format(username), attributes=['memberUid'] ) if len(conn.entries) == 0: return '', 403 # 找得到人,但没权限 return '', 200 # 成功通过 except Exception as e: print(f"[LDAP] Error during auth: {e}") return '', 500 finally: conn.unbind()这段代码做了三件事:
1. 使用传入的用户名密码尝试 Bind 到 LDAP;
2. 如果成功,再查询该用户是否出现在预设的访问组中;
3. 根据结果返回对应的状态码,由反向代理据此决定是否放行。
注意这里并没有使用管理员账号去“查找”用户是否存在,而是直接以用户身份进行 Bind。这种方式更安全,因为它不会暴露目录结构,也避免了遍历风险。同时,生产环境中应强制启用 LDAPS(端口 636),并配置 CA 证书校验,防止中间人攻击。
反向代理这边,以 Nginx 为例,配置大致如下:
location / { auth_request /auth-check; proxy_pass http://excalidraw-backend; } location = /auth-check { internal; proxy_pass http://auth-adapter/verify; proxy_method POST; proxy_set_header Content-Type "application/json"; proxy_set_body '{"username": "$remote_user", "password": "$http_authorization"}'; proxy_intercept_errors on; error_page 401 403 = @login; } # Basic Auth 挑战 location @login { return 401 "Restricted"; }当然,实际部署中还可以优化体验,比如替换 Basic Auth 弹窗为友好的 HTML 登录页,或者结合 OAuth2 Proxy 实现单点登录(SSO)集成。但对于纯 LDAP 环境来说,Basic Auth 是最简单可靠的方案。
这套架构带来的好处显而易见:
- 零代码侵入:Excalidraw 无需任何改动,未来仍可无缝升级到新版本;
- 集中管理:所有用户的增删改查均由 IT 部门通过 LDAP 统一控制,新人入职自动获得权限,离职立即失效;
- 审计溯源:Nginx 日志记录了每个请求的用户名,结合白板命名规则,可追溯谁在何时编辑了哪张图;
- 灵活授权:通过调整 LDAP 组成员,即可动态控制访问范围,无需重启服务;
- 高复用性:同一套认证网关可轻松扩展至 Grafana、Jupyter、Confluence 等其他内部系统。
更重要的是,它体现了现代 DevOps 实践中的一个重要理念:组合优于重造。我们不需要从头构建一个“企业版 Excalidraw”,而是通过标准化协议和通用中间件,把多个成熟组件拼接成一个完整解决方案。
当然,也有一些细节需要注意:
- 设置合理的连接超时(如 5 秒),避免因 LDAP 延迟导致整体响应卡顿;
- 对认证失败的请求实施 IP 级限流,防范暴力破解;
- 在 Kubernetes 环境中,使用 Secret 存储 Bind DN 密码或其他敏感配置;
- 对于临时访客(如外包人员),可通过创建临时 LDAP 组并设置 TTL 的方式实现有限期访问;
- 若需更高安全性,可在认证流程前叠加 MFA(多因素认证),例如通过 LDAP 代理层集成 TOTP 或短信验证码。
最终你会发现,一旦打通了身份认证这一关,Excalidraw 就不再只是一个“随便画画”的玩具,而真正成为一个可落地、可管理、可审计的企业级协作平台。无论是绘制系统架构图、产品原型草图,还是进行远程头脑风暴,团队都可以在一个安全可信的环境中高效协作。
而这背后的技术路径其实并不复杂:用好已有的基础设施,借力标准协议,以最小代价换取最大价值。
这种高度集成的设计思路,正在引领越来越多的开源工具走向企业核心场景。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考