ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

DzzOffice集成OnlyOffice:文档安全令牌报错排查与密钥对齐

DzzOffice集成OnlyOffice:文档安全令牌报错排查与密钥对齐 折腾 DzzOffice 的人大概都有体会这套开源协同办公系统本身的文档能力偏弱预览凑合真要上多人同时编辑的在线 Office基本都得挂一个 OnlyOffice 文档服务器上去。装完之后兴冲冲点开一个 docx页面上弹出一行字文档安全令牌未正确形成请联系您的文档服务器管理员。那个瞬间的体验就像钥匙插不进锁孔——服务进程都在跑编辑器壳子也能加载出来偏偏文档内容出不来。我在测试环境里前后处理过七八次这类故障坑位从 PHP 侧的插件配置一直排到 OnlyOffice 容器里的 JWT 开关中间还夹着版本升级带来的配置文件搬家。这篇文字就把实际排查思路和命令完整写一遍重点落在临时先把文档打开这条最快的路径上顺手把长期该怎么收口交代清楚。适合三类人看正在做 DzzOffice 加 OnlyOffice 私有化部署的运维被这行报错卡住的 PHP 开发以及虽然用的是 SpringBoot 集成 OnlyOffice、但同样被安全令牌问题绊住的 Java 同学——令牌这套逻辑和后端语言无关把它讲透哪边都用得上。1. 把报错读明白令牌问题到底卡在哪一环要解这道题先得知道 DzzOffice 和 OnlyOffice 是怎么分工的。很多人第一次搭的时候会把两者搞混以为 DzzOffice 负责存文档OnlyOffice 负责显示其实还漏了最关键的一层谁有权力打开这份文档。1.1 DzzOffice 与 OnlyOffice 的分工关系DzzOffice 是文档的家它管着文件、目录、权限、用户文档本身躺在它的存储里。OnlyOffice Document Server 是一个独立的文档渲染与协同服务它自己不知道 DzzOffice 里有哪些人、哪些文件它只认一个东西打开文档的请求里带上来的参数。所以整个流程是——用户点开 DzzOffice 里的一个文件DzzOffice 后端PHP生成一段配置把文件下载地址、回调地址、编辑模式、用户信息打包交给前端页面前端拿着这段配置去请求 OnlyOffice 的 api.jsOnlyOffice 再按配置去拉文件、渲染、在有人保存时回调 DzzOffice 的地址。这条链路里OnlyOffice 是外人它默认不信任任何调用方。JWT 安全令牌就是它用来验证这个请求确实来自我认可的 DzzOffice的凭据。1.2 一次文档打开背后的三次令牌交互真正的令牌校验不是一次而是三次这是很多人忽略的地方浏览器打开文档时DzzOffice 前端把配置连同token字段一起发给 OnlyOfficeOnlyOffice 用密钥验签确认这段配置没被篡改、来源可信。OnlyOffice 拉取文件时如果是服务端方式取文件请求头里也要带令牌。OnlyOffice 回调保存时编辑完成、文档关闭OnlyOffice 主动 POST 到 DzzOffice 的回调地址这次请求同样要带令牌DzzOffice 反过来验一遍。三步里任何一步的密钥对不上都会出现类似的令牌报错。而你看到的那句文档安全令牌未正确形成通常出现在第一步或者第三步。1.3 报错文案本身透露的信息未正确形成这几个字其实很讲究。它不是说令牌错了而是说根本没形成一个可校验的令牌。区别在哪前者是密钥不匹配你签了但两边密钥不一样后者往往是压根没签——DzzOffice 送过来的配置里没有token字段而 OnlyOffice 那边开启了强制校验一看没有令牌直接拒绝返回的就是这句话。判断方向上有个小技巧打开浏览器 F12看 Network 里跟api.js、docbuilder相关的请求把请求体里的配置对象扒出来看有没有token这个 key。没有就是没形成有但报错就是密钥不匹配或者签名算法不对。这一步定位清楚后面省一半时间。2. 为什么刚装完就报错版本与开关的错位故障出现的时间点很关键。装完就打不开和用了一段时间突然打不开是两种完全不同的病因。前者几乎可以锁定为一件事新版 OnlyOffice 默认开了 JWT而老的集成插件没跟上。2.1 新版默认打开 JWT 的机制OnlyOffice Document Server 从 7.2 版本往后JWT 令牌校验默认就是开启状态社区版也一样。它的逻辑很直白你没显式关掉我就当成开着任何没带令牌的请求一律拒绝。而 DzzOffice 应用市场里能装的那套 OnlyOffice 插件历史版本不少是不带令牌生成能力的。也就是说插件只管拼配置、发请求从来不知道世界上还有 JWT 这回事。两边一碰报错几乎是必然的。注意判断插件是否支持令牌最直接的办法是翻插件目录里的 PHP 源码搜firebase、JWT、token这几个关键字。搜不到基本可以确定它不会签名这时候要么改插件要么从服务端关掉校验。2.2 配置文件从 default.json 迁到 local.json 的坑这是版本升级带来的第二个坑。老教程会告诉你改/etc/onlyoffice/documentserver/default.json照着改完之后重启发现没生效——因为从 7.2 开始官方把用户可覆盖的配置挪到了同目录的local.jsondefault.json里的值在启动时会被local.json里的内容覆盖。所以会出现一种很迷惑的现象你改的确实是官方文档里写的那个文件命令也没敲错重启也做了报错纹丝不动。不是你手残是改错了地方。当前版本改local.json才是正解default.json当参考读物就好。2.3 时间戳与时钟漂移造成的伪失败还有一种概率低但很恶心的情形密钥是对的令牌也生成了但服务端校验失败。这时候要怀疑时间。JWT 里通常会带签发时间iat、过期时间expOnlyOffice 校验时会跟本机时间比对。如果 DzzOffice 所在服务器和 OnlyOffice 容器的时间差了十几分钟甚至几小时令牌要么被判过期要么被判来自未来。容器常见的问题是宿主机休眠后容器时间没跟上或者时区设置成了 UTC 而两边理解不一致。检查方式很简单分别在两边敲一遍date看输出差多少# DzzOffice 所在主机 date # OnlyOffice 容器内 docker exec -it 容器名 date两个时间差超过一分钟就先把时间同步做掉再回头看令牌问题。3. 临时解决办法先关掉令牌校验把文档打开标题里说的是临时解决办法那就先把最直接的一条路走完在 OnlyOffice 服务端关闭令牌校验。这样做的代价后面会讲现在只关注怎么改、怎么重启、怎么验证。3.1 Docker 部署的完整操作流Docker 部署是绝大多数人的选择官方镜像onlyoffice/documentserver拉起来就是一个能用的文档服务器。操作步骤如下第一步先备份别嫌麻烦配置写坏了服务直接起不来docker exec -it 容器名 cp /etc/onlyoffice/documentserver/local.json /etc/onlyoffice/documentserver/local.json.bak第二步把文件取到宿主机上改比在容器里用 vi 舒服得多docker cp 容器名:/etc/onlyoffice/documentserver/local.json ./local.json第三步编辑local.json。如果文件是空的或者只有一对花括号直接写入下面这段如果里面已经有内容把token相关的部分按这个结构合并进去不要整段覆盖掉原有的数据库、日志配置{ services: { CoAuthoring: { token: { enable: { request: { inbox: false, outbox: false }, browser: false } } } } }这三个开关的分工要说明白browser管的是前端打开文档时那段配置的校验request.inbox管的是 OnlyOffice 收到的请求request.outbox管的是它主动发出去的回调。只关browser能打开文档但一保存就崩所以三个一起关。第四步把文件塞回去并重启docker cp ./local.json 容器名:/etc/onlyoffice/documentserver/local.json docker exec -it 容器名 supervisorctl restart all用supervisorctl restart all是因为 OnlyOffice 内部有 docservice、converter、metrics 好几个进程都由 supervisor 管着只重启 nginx 是不够的配置不会重新加载。重启完等十几秒让服务把缓存吐出来再测。3.2 裸机/包管理器安装的改法如果你是用 deb 包或者 rpm 包直接装在宿主机上的路径一样只是没有 docker 前缀cp /etc/onlyoffice/documentserver/local.json /etc/onlyoffice/documentserver/local.json.bak vi /etc/onlyoffice/documentserver/local.json supervisorctl restart all有些发行版上服务名是ds-docservice、ds-converter那就用systemctl restart ds-docservice ds-converter ds-metrics改完一样要验证。裸机部署有个额外注意点文件权限。改配置的时候如果用 root 写了supervisor 起进程的用户读不到会静默失败日志里能看到权限拒绝。稳妥做法是改完chown回原来的属主或者直接用sudo -u切到对应账号编辑。3.3 改完必须做的三项验证改完配置不看结果就以为万事大吉是新手最容易翻车的地方。我一般做三步验证第一确认配置真的被加载了。访问http://你的文档服务器地址/healthcheck返回true说明服务活着。更狠一点直接进容器搜一下当前生效的配置docker exec -it 容器名 grep -A 5 token /etc/onlyoffice/documentserver/local.json第二回到 DzzOffice 页面用无痕窗口重新打开一份文档。为什么要无痕因为浏览器缓存了之前带报错的 api.js 响应不清缓存会看到旧报错白白怀疑人生。第三打开 F12 看 Network找到那个返回文档配置的请求确认返回体结构里error字段没了文档区域开始正常渲染。这一步比肉眼看页面靠谱得多页面渲染有延迟网络面板是实时的。3.4 这个临时方案的安全代价必须把话说透关掉 JWT 之后你的文档服务器对任何能访问到它的人都是敞开的。别人只要知道文档的下载地址和回调地址就能构造请求打开文档、触发回调甚至可以伪造保存动作往你的 DzzOffice 里写东西。所以这个方案只适合三种场合内网隔离的测试环境、文档服务器没做公网映射、故障应急期间临时恢复业务。真要在生产环境长期跑还是得回到密钥对齐那条路上。把先关再配当成一个两段式的动作而不是终点。4. 顺手做对长期方案两端密钥对齐关令牌只是把问题按下去没有解决。原理上正确的做法是两边用同一个密钥DzzOffice 负责签OnlyOffice 负责验。这段看起来复杂实际动手就三步。4.1 造一个够硬的 secret密钥别用123456、onlyoffice这种也别用站点域名。用系统自带的随机工具生成一串足够长的十六进制openssl rand -hex 32这条命令吐出 64 个字符作为 HS256 的密钥强度足够。记下来这就是两端共用的那根钥匙。生成完别贴在聊天工具里传来传去直接在两台机器上分别配置。4.2 DzzOffice 后台的填写位置与常见填错DzzOffice 的 OnlyOffice 插件通常在后台的应用管理或者插件设置页里有文档服务器地址和一个密钥输入框。把上面的 secret 原样贴进密钥框保存。这里有几个高频填错的地方我按踩坑概率排个序多复制了空格或换行。从终端复制的时候特别容易带上尾随换行粘进去之后肉眼看不出来签名必然不一致。把密钥填到了错误的分支。有的插件版本里密钥分请求密钥和浏览器密钥两块只填一块另一块为空保存能成功但保存文档时报错。地址填的是内网地址浏览器访问不到。文档服务器地址必须是浏览器能直连的地址因为它最终是浏览器去请求 api.js不是 DzzOffice 后端去请求。填成http://127.0.0.1或者容器内网 IP页面永远转圈。4.3 密钥对齐后的自检清单配置完别急着点保存先按这个清单过一遍检查项正确状态常见错误两端密钥字符串完全一致无空格换行复制时带入不可见字符OnlyOffice 端位置local.json中的 token 分支改到了 default.json浏览器开关browser.enable为 true关着但期望校验生效服务重启supervisorctl restart all 完成只重启 nginx文档服务器地址浏览器可直接访问的地址用了 127.0.0.1 或容器内网 IP时间同步两端时差小于一分钟容器时间漂移这张表看着简单实际排查时能覆盖八成以上的配了还是不生效。5. 排查实录日志、抓包和几个典型故障前面讲的是怎么办这一节讲怎么查。故障现场的信息量其实很大只是很多人不知道该看哪儿。5.1 该看哪几个日志OnlyOffice 的日志分散在几个目录按重要性排序# 主服务日志令牌校验失败最先在这里冒头 docker exec -it 容器名 tail -f /var/log/onlyoffice/documentserver/docservice/out.log # 转换服务文档打不开但没报令牌错时看这个 docker exec -it 容器名 tail -f /var/log/onlyoffice/documentserver/converter/out.log # nginx 访问日志看请求到底有没有打到服务上 docker exec -it 容器名 tail -f /var/log/nginx/access.logdocservice/out.log里出现token、signature、JWT字样的行基本就是问题所在。converter/out.log里如果全是字体报错那是另一个方向的问题——文档能打开但排版乱跟令牌无关。DzzOffice 侧则要开 PHP 的错误日志看插件在拼配置时有没有抛异常。很多插件的令牌逻辑写得比较糙失败之后不报错直接静默生成一段不带 token 的配置前端只看到结果看不到过程。5.2 常见故障速查表现象可能原因处理方向提示安全令牌未正确形成服务端开了 JWT插件不带令牌关校验或升级插件能打开但不能保存只关了 browser 校验回调仍在校验request.inbox/outbox 一并关闭或配密钥改了配置无效果改的是 default.json改 local.json 并完整重启文档加载后空白文件下载地址 OnlyOffice 访问不到检查文件地址是否为外网可解析地址页面一直转圈无报错api.js 地址不可达用浏览器直接压测 api.js 地址保存后内容丢失回调地址不可达确保 OnlyOffice 能回连 DzzOffice这张表建议直接抄进运维手册下次再遇到能省掉从零排查的时间。5.3 我踩过的三个坑第一个坑改了 local.json 但没重启全部进程。当时只重启了 nginx页面上的错误变了但没消失我以为配置写错了来回折腾一个小时最后发现是缓存。教训是OnlyOffice 的配置生效依赖 supervisor 管的全部子进程重启少一个都不行。第二个坑测试环境和生产环境共用同一个文档服务器。测试环境插件版本老把 JWT 关掉了结果这个文档服务器对生产也是敞开的两边配置互相干扰。后来我把测试和生产彻底拆成两套容器各自独立配置问题再没复现过。第三个坑密钥里的特殊字符被转义。用openssl rand -hex生成的密钥是安全的但有一次我图省事用了带和/的 base64 串插件在 JSON 编码时把它转义了导致签名对不上。这件事之后我固定只用十六进制密钥省心。5.4 逆向连接器请求格式的自查思路有时候你手上没有插件的文档也不确定它到底带没带令牌。这时候可以走观察请求的路子打开浏览器开发者工具在页面发起文档加载前开启网络录制把编辑器相关的请求全部抓下来重点看文档配置那段 JSON。如果配置体里完全没有token字段那就确认是插件侧的问题方向转到服务端关闭校验或者改造插件如果token存在但被拒那就要去看签名算法是不是 HS256、密钥是不是一致。这个思路本质上是对连接器交互格式做一次静态观察不涉及任何破解行为纯粹是排障手段我自己用过很多次比翻源码快得多。6. 几套环境跑下来的一点个人体会私有化部署在线文档这件事坑位密度最高的从来不是文档本身能不能渲染而是两端打招呼的那几行参数。DzzOffice 加 OnlyOffice 的组合尤其明显一个 PHP 的老牌协同系统配一个迭代很快的文档服务版本节奏一错位令牌这种后来才加的校验就成了最常见的拦路石。我自己的做法是任何一次升级 OnlyOffice 之前先把local.json整个备份出来升级完对比一遍差异看看 token 分支有没有被重置。有几次升级之后配置被覆盖回默认值就是因为没做这一步半夜又被叫起来处理。另一个经验是无论临时关校验还是正式配密钥改完之后都用同一个测试文档走一遍完整链路打开、编辑、保存、再打开确认内容在。只测打开不测保存等于只做了一半验证回调那段的坑会在你最不希望的时候冒出来。至于网上那些一键脚本自动配置助手我的态度是可以用来看思路但别直接跑在生产上。这类脚本多半只处理了 Docker 这一种部署形态遇到反向代理、多实例、自定义端口就歇菜而且它们改了哪些文件往往没有清晰的日志出问题之后反倒更难回滚。手工改一遍哪怕慢十分钟对这套系统的理解会扎实很多下次遇到同类问题二十秒就能定位。
返回列表