Excalidraw SSO单点登录集成方案
在现代企业中,一个看似简单的绘图工具也可能成为安全链条上的关键一环。Excalidraw 作为一款广受欢迎的开源手绘风格白板工具,因其直观的操作和出色的协作能力,正被越来越多团队用于架构设计、流程梳理和头脑风暴。然而,当它从个人使用走向企业部署时,一个问题立刻浮现:如何确保只有授权员工才能访问那些可能包含敏感信息的设计图?
想象一下这样的场景:某工程师在 Excalidraw 上绘制了公司核心系统的架构图,并通过链接分享给同事。如果这个链接被未认证用户打开,甚至被搜索引擎抓取,后果不堪设想。更糟糕的是,很多团队仍在使用默认配置——允许完全匿名访问。这就像把会议室的门敞开,任何人都可以进来翻看白板上的内容。
正是在这种背景下,单点登录(SSO)集成不再是“锦上添花”,而是企业级部署的刚需。它不仅解决安全性问题,还能统一身份管理,让 Excalidraw 真正融入企业的 IT 生态体系。
Excalidraw 本身是一个典型的无状态前端应用,基于 React 和 Canvas 构建,协作功能依赖 WebSocket 或轮询机制同步状态。它的原始设计并不包含用户系统,所有数据要么保留在本地,要么临时存于内存中。这种轻量化的架构让它启动迅速、易于嵌入,但也意味着任何安全控制都必须通过外部手段实现。
企业在引入 SSO 时,通常面临两个选择:一是改造前端代码,直接集成认证逻辑;二是利用反向代理,在不改动源码的前提下完成统一鉴权。前者灵活但维护成本高,后者快速但对定制化支持有限。实际落地中,多数工程团队倾向于后者,尤其是当他们希望保持上游镜像纯净、便于后续升级时。
这里的关键在于理解 SSO 的工作原理。我们常说的 SSO,其技术底座通常是OAuth 2.0和OpenID Connect(OIDC)。其中 OAuth 2.0 负责授权,而 OIDC 在其基础上增加了身份认证层,返回标准化的 JWT Token,包含用户身份信息如email、name等。当你点击“用公司账号登录”时,背后就是浏览器重定向到 Identity Provider(IdP),比如 Okta、Azure AD 或 Keycloak,完成认证后再跳回原应用的过程。
整个流程中最容易出错的地方,往往不是协议本身,而是实现方式的选择。例如,是否应该在前端直接处理 token?答案是:尽量避免。虽然像oidc-client-js这样的库可以让前端轻松发起 OIDC 流程,但如果采用隐式流(implicit flow),access token 会暴露在 URL 中,存在 XSS 风险。更安全的做法是使用授权码模式 + PKCE(Proof Key for Code Exchange),并将 code 的交换过程放在后端完成。这样即使前端被注入恶意脚本,也无法获取最终的 access token。
来看一个典型的安全实践:
import { UserManager } from 'oidc-client'; const userManager = new UserManager({ authority: 'https://yourcompany.okta.com', client_id: '0oa123abc456def789', redirect_uri: 'https://excalidraw.yourcompany.com/callback', response_type: 'code', scope: 'openid profile email', post_logout_redirect_uri: 'https://excalidraw.yourcompany.com', });这段代码看起来简单,但它只是整个认证流程的起点。真正关键的是回调处理环节。生产环境中,/callback接口不应由前端直接处理,而应由一个轻量级后端服务接收code,然后通过 client_secret 与 IdP 交换 token。这样做不仅能防止 token 泄露,还能在服务端验证 JWT 的签名、issuer、audience 等声明,杜绝伪造请求。
不过,如果你不想自己写后端逻辑,还有一个更省事的办法:用反向代理接管认证。
像OAuth2 Proxy、Authentik或Keycloak Gatekeeper这类工具,可以在 Nginx 或独立容器前加一层“认证网关”。所有对 Excalidraw 的访问请求都会先经过它,如果没有有效会话,就会被重定向到企业登录页。一旦认证成功,代理会生成一个加密 Cookie,并将用户信息以 HTTP Header 的形式注入请求,比如:
X-Forwarded-User: alice@company.com X-Forwarded-Email: alice@company.com X-Forwarded-Preferred-Username: Alice Chen这些头部可以被后端协作服务读取,用于标识用户身份、记录操作日志,甚至做细粒度权限控制。最重要的是,Excalidraw 本身的镜像完全不需要修改,你可以继续使用官方的excalidraw/excalidraw:latest,只需在部署时加上代理层即可。
下面是一个基于 Docker Compose 的典型配置示例:
version: '3' services: excalidraw: image: excalidraw/excalidraw:latest ports: - "8080" networks: - proxy-network oauth2-proxy: image: bitnami/oauth2-proxy:latest command: | --provider=oidc --oidc-issuer-url=https://yourcompany.okta.com --client-id=0oa123abc456def789 --client-secret=XXXXXX --cookie-secret=YYYYYY --email-domain=yourcompany.com --upstream=http://excalidraw:80 --http-address=0.0.0.0:4180 --reverse-proxy=true --set-xauthrequest=true --pass-user-headers=true ports: - "443:4180" environment: - OAUTH2_PROXY_CLIENT_ID - OAUTH2_PROXY_CLIENT_SECRET networks: - proxy-network networks: proxy-network: driver: bridge这套组合拳的好处显而易见:部署快、风险低、可复用。你甚至可以为多个内部工具(如 Grafana、Wiki、CI/CD 页面)共用同一个 OAuth2 Proxy 实例,只需调整路由规则即可。而且,由于所有流量都经过代理,天然具备集中日志记录能力,方便审计和排查问题。
当然,这种方案也不是没有挑战。最大的坑通常出现在WebSocket 协议兼容性上。Excalidraw 的实时协作依赖 WebSocket 连接,而许多反向代理默认不会转发 Upgrade 请求头,导致 WebSocket 握手失败。解决方法是在代理配置中显式启用 WebSocket 支持,例如在 Nginx 中添加:
location / { proxy_pass http://excalidraw:80; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header Host $host; }同时,Cookie 的SameSite属性也需要特别注意。如果 Excalidraw 是以 iframe 形式嵌入到企业门户中,而两者域名不同,那么传统的 Lax 模式会导致 Cookie 不发送,用户始终无法认证。此时应将 Cookie 设置为SameSite=None; Secure,并确保全程使用 HTTPS。
另一个常被忽视的问题是会话生命周期管理。企业员工出差、换设备、长时间离席等情况频繁发生,如果会话永不过期,一旦设备丢失就会造成安全隐患。合理的做法是设置较短的 idle timeout(如 30 分钟),并在 IdP 端支持全局登出(logout all devices)。当 HR 系统标记某员工离职时,IT 可立即通过 IdP 注销其所有活跃会话,切断对 Excalidraw 的访问权限——这才是真正的“一键禁用”。
从系统架构上看,完整的集成方案应该是这样的:
+------------------+ +--------------------+ +---------------------+ | User Browser | <---> | Reverse Proxy + | <---> | Excalidraw Frontend | | | | OAuth2 Proxy / | | (Static Assets) | | | | Auth Middleware | | | +------------------+ +--------------------+ +----------+----------+ | v +---------------------------+ | Real-time Collaboration | | Backend (WebSocket Server)| +---------------------------+ | v +----------------------------+ | Persistence Layer | | - Database (e.g., PostgreSQL) | - Object Storage (e.g., S3) +----------------------------+ +------------+ | Identity | | Provider | | (e.g., Okta)| +------------+在这个模型中,反向代理承担了第一道防线的角色,所有 HTTP 和 WebSocket 请求都要先过它这一关。认证通过后,用户信息通过 Header 下发,后端协作服务据此建立带身份的会话。持久化层则负责保存白板数据,并关联创建者信息,确保每一份文档都有迹可循。
实际落地时,还有一些细节值得推敲。比如,是否要开放部分白板为“只读公开”?如果是,可以通过代理配置实现路径级控制:/public/*免认证,/private/*强制 SSO。又比如,是否需要支持多租户?那就在 IdP 中按部门划分 group claim,再在代理或后端做 RBAC 判断。
最终带来的价值远超技术本身。一个集成了 SSO 的 Excalidraw,不再只是一个绘图工具,而是变成了组织知识资产的一部分。每一次编辑都被记录,每一个访问都被追踪,每一次分享都在可控范围内。它帮助企业实现了三个关键跃迁:
- 从“谁都能进”到“仅限可信人员”:彻底杜绝未授权访问;
- 从“记不住是谁改的”到“责任到人”:所有操作可追溯;
- 从“各自为政”到“统一入口”:与 Jira、GitLab 等系统共享同一套登录体验。
对于运维团队来说,这也意味着更少的账号管理工作。再也不用担心某个实习生离职后仍能打开旧链接,因为只要在 AD/LDAP 中禁用账户,他在所有集成 SSO 的系统中都会自动失效。
这种高度集成的设计思路,正引领着轻量级协作工具向更安全、更可控的方向演进。Excalidraw 的案例告诉我们,哪怕是最简单的应用,只要站在企业级安全的视角重新审视,也能焕发出新的生命力。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考