DzzOffice 里把 OnlyOffice 装上点开一个文档编辑器没出来页面上直接甩一句“文档安全令牌未正确形成”这种场面我见过不止一次。第一次遇到时我以为是 OnlyOffice 没装好重装了两次最后发现根本不是安装问题而是 DzzOffice 和 Document Server 之间的令牌握手没对上。这个报错在 onlyoffice安装问题里非常典型尤其是你已经在用 DzzOffice 做在线编辑文书、又想让 OnlyOffice 承担预览和协作编辑的时候。它解决的痛点很明确先让文档能打开而不是卡在权限校验门口。适合谁看正在折腾 DzzOffice OnlyOffice 集成的运维、后端、PHP 开发者以及用 SpringBoot 集成 OnlyOffice 或做 onlyoffice在线编辑 的同行。下面我按临时解决办法来写先把路走通再谈后面怎么修正规。1. 先搞清楚 dzzoffice 与 onlyoffice 的令牌到底卡在哪1.1 报错现场不是文档打不开而是握手没通过“文档安全令牌未正确形成”这句话一般不是 DzzOffice 自己弹的而是 OnlyOffice Document Server 返回给前端的。你看到的页面可能还停在 DzzOffice 的文档列表或者已经跳到编辑器容器但编辑器区域是空白控制台里能看到 403、500或者一条关于 token 的报错。很多人第一反应是文档地址错了、权限不对、OnlyOffice 没启动实际上文档可能完全正常DzzOffice 也能读到文件问题出在打开编辑器之前的 config 请求上。OnlyOffice 打开文档时浏览器会先向 Document Server 请求编辑器脚本然后带着一个 config 去初始化。这个 config 里通常包含文档类型、文件地址、权限、用户信息、回调地址、编辑器界面语言等。如果 Document Server 开启了令牌校验它还会要求这个 config 里带一个 JWT或者请求头里带 Authorization。DzzOffice 负责生成这个 JWTDocument Server 负责验证。验证不过就直接拒绝初始化报错就是“文档安全令牌未正确形成”。所以这个问题的本质不是“文档损坏”也不是“OnlyOffice 安装失败”而是两套系统在密钥、算法、字段、时间、传输链路上没有对齐。临时解决办法的目标就很清楚了要么让 Document Server 暂时不校验要么让 DzzOffice 生成一个能被 Document Server 认可的令牌。1.2 令牌生成的三个关键角色DzzOffice、Document Server、浏览器先把链路拆开。DzzOffice 是业务入口它知道当前用户是谁、能打开哪个文件、文件在哪个存储位置。OnlyOffice Document Server 是编辑器服务它只认 config 和 token不关心你在 DzzOffice 里是什么角色。浏览器夹在中间它拿到 DzzOffice 给的页面和 config再去请求 Document Server。令牌校验正常时流程大体是这样用户在 DzzOffice 点击文档。DzzOffice 根据当前文件、用户、权限生成一份 config。DzzOffice 用双方约定好的 secret对 config 或相关 payload 做 JWT 签名。DzzOffice 把 config 和 token 一起交给浏览器。浏览器请求 OnlyOffice Document Server。Document Server 用同一个 secret 验证 token。验证通过后编辑器加载文档后续保存时再通过回调地址通知 DzzOffice。失败通常发生在第 3 步到第 6 步之间。最常见的是 secret 不一致DzzOffice 插件里填的是 AOnlyOffice 的 local.json 里是 B签名和验签用的根本不是同一把钥匙。第二种是字段不完整JWT 只签了用户 ID没有签 document、editorConfig、permissions 等关键字段Document Server 认为 token 和 config 对不上。第三种是传输丢失反向代理把 Authorization 头吃掉了或者 token 放在 URL 里被编码坏了。第四种是时间不同步JWT 的 exp 过期或者 iat 比服务器时间快太多验签直接失败。JWT 本身是三段式header.payload.signature。header 说明算法常见是 HS256payload 放声明signature 是用 secret 算出来的签名。任何一段被改变、secret 不对、算法不匹配都会导致“未正确形成”。这也是为什么只改一个字符都不行密钥前后的空格、换行、引号都可能让结果完全不同。1.3 为什么临时关闭校验能先把文档打开临时解决办法里最快的一种就是让 OnlyOffice Document Server 关闭令牌校验。关闭后Document Server 不再检查 config 里的 token也不要求 Authorization。DzzOffice 发过来的 config 只要格式正确、文件地址可达编辑器就能打开。对于内网环境、测试环境、或者是业务急着要用的场景这一招通常几分钟就能见效。但必须说清楚这是临时办法不是长期方案。关闭令牌校验等于把 Document Server 的 config 接口暴露给任何能访问它的人。只要别人知道文件 URL或者能构造 config就可能绕过 DzzOffice 的权限控制。尤其是 Document Server 直接暴露在公网、又没有额外访问控制时风险很高。我的习惯是只在隔离内网、临时排障、或者刚刚迁移完服务时关闭校验排障结束后马上恢复并把 secret 重新对齐。另外关闭校验不代表回调地址、文件地址、跨域配置就自动正确。编辑器能打开不等于保存一定成功。如果 DzzOffice 和 Document Server 之间的回调 URL 不通用户编辑完点保存还是会失败。所以临时关闭校验只是把“打开文档”这一步先放行后面的保存链路要单独查。2. 临时解决办法一在 OnlyOffice 端关闭令牌校验2.1 找到配置文件并先备份OnlyOffice Document Server 的配置通常分两层默认配置在default.json本地覆盖在local.json。你直接改 default.json 也能生效但升级或重装时容易被覆盖所以更推荐写local.json。在常见的 Linux 安装方式下路径是/etc/onlyoffice/documentserver/local.json如果这个文件不存在可以自己创建。Docker 部署的 OnlyOffice路径通常在容器内同样的位置你需要先进入容器docker exec -it onlyoffice-documentserver bash ls -l /etc/onlyoffice/documentserver/改之前一定先备份别嫌麻烦cp /etc/onlyoffice/documentserver/local.json /etc/onlyoffice/documentserver/local.json.bak如果没有 local.json也可以先备份 default.json虽然不推荐直接改它。备份的意义在于临时方案失效或者要恢复校验时你能快速回到原状态。我踩过一次坑手快改了 default.json后来升级 Document Server配置被覆盖令牌校验又开了DzzOffice 那边却没改结果又报同样的错。从那以后我只在 local.json 里做覆盖。2.2 写入关闭令牌的配置关闭令牌校验的核心配置在services.CoAuthoring.token下面。一个常见的 local.json 写法如下{ services: { CoAuthoring: { token: { enabled: false, secret: { browser: { string: }, inbox: { string: }, outbox: { string: }, session: { string: } } } } } }这里有几个细节要注意。第一字段名是enabled不是enable。有些旧文章写成enable新版本不认改完等于没改。第二secret 的四个 string 建议一起清空尤其是 browser。只把 enabled 改成 false、secret 还留着旧值某些版本下仍可能出问题。第三JSON 格式必须严格逗号、引号、括号都不能错。改完可以用python -m json.tool或jq检查python -m json.tool /etc/onlyoffice/documentserver/local.json jq . /etc/onlyoffice/documentserver/local.json如果 JSON 不合法Document Server 可能启动失败或者继续用旧配置。我见过最隐蔽的一次是复制配置时多了一个中文引号肉眼看不出来服务重启后日志里才报解析错误。注意关闭令牌校验只适合内网临时排障。公网环境不要长期保持关闭否则任何能访问 Document Server 的人都有可能绕过业务系统的权限。2.3 重启服务并确认配置真的生效改完配置必须重启。不同安装方式命令不一样# 普通 Linux 安装 systemctl restart onlyoffice-documentserver # 使用 supervisor 的旧版本 supervisorctl restart all # Docker 部署 docker restart onlyoffice-documentserver重启后看服务状态和日志systemctl status onlyoffice-documentserver tail -f /var/log/onlyoffice/documentserver/docservice/out.log tail -f /var/log/onlyoffice/documentserver/converter/out.log如果是 Dockerdocker logs -f onlyoffice-documentserver然后访问健康检查接口curl http://127.0.0.1:8080/healthcheck返回true一般说明服务起来了。接下来回到 DzzOffice强制刷新浏览器最好用无痕窗口再试。如果 DzzOffice 有缓存后台清一下缓存或者删掉data/cache下的相关缓存文件。很多时候配置已经生效但浏览器还在用旧的编辑器 iframe看起来像没改。如果还是报“文档安全令牌未正确形成”先别怀疑配置没写对去看 Document Server 日志里有没有 token 相关记录。如果日志里仍然出现 JWT 校验失败说明你改的 local.json 没被加载或者还有第二个 Document Server 实例在提供服务。检查 Nginx 转发到了哪个后端检查 Docker 是否有多个容器检查是否有负载均衡。3. 临时解决办法二让 DzzOffice 与 OnlyOffice 的密钥重新对齐3.1 DzzOffice 插件里的配置项怎么填如果你不想关闭 OnlyOffice 的令牌校验或者关闭后仍然希望走正规链路那就需要把 DzzOffice 这边的密钥和 OnlyOffice 对齐。DzzOffice 通常在后台“应用管理”或“应用市场”里安装 OnlyOffice 连接器然后在插件设置里填 Document Server 地址和密钥。典型配置项包括配置项说明常见错误Document Server 地址OnlyOffice 服务地址多了或少了一个斜杠协议写错密钥与 OnlyOffice 的 secret 一致前后有空格、换行或填了错误的值回调地址DzzOffice 可被 OnlyOffice 访问的地址填了 127.0.0.1Document Server 访问不到编辑器语言界面语言多语言场景下与实际用户不匹配文件存储地址文档真实地址外网地址和内网地址混用密钥对齐的关键是DzzOffice 插件里填的 secret必须和 OnlyOfficelocal.json里services.CoAuthoring.secret.browser.string完全一致。注意是完全一致大小写、空格、换行都算。我遇到过最离谱的一次是运维在密钥末尾多敲了一个空格前端看不出来JWT 签名就是不对。后来用sed -n l打印不可见字符才找到。如果你已经决定临时关闭 OnlyOffice 的令牌校验那么 DzzOffice 这边的密钥可以留空。但有些版本的插件如果检测到密钥为空会拒绝生成 config或者仍然尝试拼接 token。所以关闭校验后最好把 DzzOffice 插件里的密钥也清掉保存再清缓存。两边状态要一致OnlyOffice 不校验DzzOffice 不强制签这样最省事。3.2 JWT 生成代码的常见错误与修补位置DzzOffice 的 OnlyOffice 连接器通常是 PHP 写的里面会调用 JWT 库生成 token。常见库是firebase/php-jwt。如果你需要临时修代码先找到生成 config 和 token 的文件。插件目录一般在 DzzOffice 的dzz或core相关目录下名字里可能带onlyoffice。你可以用 grep 搜grep -R JWT /path/to/dzzoffice --include*.php grep -R onlyoffice /path/to/dzzoffice --include*.php grep -R secret /path/to/dzzoffice --include*.php找到之后重点看几个地方secret 是不是硬编码的和 OnlyOffice 当前 secret 是否一致。JWT 算法是不是 HS256OnlyOffice 是否支持。payload 是否包含 config 里的关键字段还是只签了一个空的数组。token 是放在 config 的token字段还是放在请求头 Authorization 里。生成 token 之前config 是否已经被修改过导致签名和实际内容不一致。一个 PHP 侧的正确思路大致如下use Firebase\JWT\JWT; $config [ document [ fileType docx, key $fileKey, title $fileName, url $fileUrl, ], editorConfig [ mode edit, lang zh-CN, user [ id (string)$userId, name $userName, ], callbackUrl $callbackUrl, ], ]; $secret 这里填与 OnlyOffice 一致的密钥; $token JWT::encode($config, $secret, HS256); $config[token] $token;这里的关键是JWT 的 payload 要和 config 对应。不要只签一个userId然后指望 Document Server 放行。Document Server 会检查 token 里的声明和实际 config 是否一致。你签的内容越完整越不容易出问题。如果只是临时让文档打开也可以把整个 config 作为 payload 签进去再把 token 塞回 config。另一个常见错误是算法不匹配。DzzOffice 插件用 HS256 签OnlyOffice 配置却要求 HS512或者反过来。一般 HS256 够用双方一致即可。还有 secret 的处理有的代码会先 base64_decode有的直接用字符串。如果一边 decode 一边不 decode签名必然对不上。临时修的时候先确认 secret 的原始字符串是什么不要想当然。3.3 时间同步、URL、协议与反向代理的隐藏影响令牌校验不只看 secret还看时间。JWT 里通常有iat和exp。如果 DzzOffice 服务器时间比 OnlyOffice 慢太多或者快太多Document Server 可能认为 token 还没生效或已经过期。先查时间date timedatectl两台服务器最好都开启时间同步。内网环境如果没有外网时间源至少保证两台机器时间差在几分钟以内。我遇到过一台测试机时间停在几个月前JWT 一生成就过期报错却只说令牌未正确形成查了半天才发现是时间问题。URL 和协议也很关键。DzzOffice 生成 config 时如果填的是http://内网IP而浏览器访问的是https://域名Document Server 可能认为文档地址和来源不一致。反向代理场景下要确保 Nginx 把Host、X-Forwarded-Proto、X-Forwarded-For、Authorization这些头正确传下去。一个常见的 Nginx 片段如下location /onlyoffice/ { proxy_pass http://127.0.0.1:8080/; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection upgrade; proxy_set_header Authorization $http_authorization; }如果Authorization被代理吃掉token 就传不到 Document Server自然报错。回调地址也必须从 Document Server 所在机器能访问到。DzzOffice 如果填127.0.0.1Document Server 在另一台机器或容器里回调就会失败。临时排障时可以在 Document Server 容器里curl一下 DzzOffice 的回调地址确认网络通。4. 实操复盘从报错到打开文档的完整记录4.1 排查顺序先看 OnlyOffice 日志再看浏览器请求我一般按这个顺序排查效率最高看 OnlyOffice Document Server 日志确认是不是 token 校验失败。看浏览器 Network确认 config 请求和响应。看 DzzOffice 插件配置确认地址和密钥。看反向代理配置确认请求头没有丢。最后才去改代码或关闭校验。日志命令tail -n 200 /var/log/onlyoffice/documentserver/docservice/out.log grep -i token\|jwt\|signature\|forbidden /var/log/onlyoffice/documentserver/docservice/out.log如果看到invalid signature、jwt malformed、token is not correctly formed基本就是密钥或 payload 问题。如果看到connect ECONNREFUSED那是回调地址或文件地址网络不通不是令牌本身的问题。4.2 浏览器 Network 面板里应该看到什么打开浏览器开发者工具切到 Network勾选 Preserve log然后点开文档。重点看几个请求请求 Document Server 的api.js是否 200。请求 config 的接口是否 200响应里有没有token字段。请求编辑器初始化接口是否 403 或 500。响应体里有没有“文档安全令牌未正确形成”的原文。如果 config 接口返回 200但编辑器接口 403大概率是 token 没带过去或者签名不对。如果 config 接口就报错说明 DzzOffice 自己生成 config 时已经出问题。可以复制请求的 token到 JWT 调试工具里解码看看 payload 是否完整、exp 是否过期。注意不要在生产环境随便把 token 发到第三方网站本地解码就行。4.3 最终配置示例与重启清单临时关闭校验时我的最终配置通常保持这样{ services: { CoAuthoring: { token: { enabled: false, secret: { browser: { string: }, inbox: { string: }, outbox: { string: }, session: { string: } } } } } }DzzOffice 插件里 Document Server 地址填实际可访问地址密钥留空保存后清缓存。重启清单systemctl restart onlyoffice-documentserver systemctl restart nginx # 如果有 PHP-FPM也重启一下 systemctl restart php-fpm然后无痕窗口测试。如果打开成功先别急着庆祝继续测试编辑、保存、多人协作。保存成功才说明回调链路也通。5. 常见问题速查与避坑清单5.1 常见问题速查表现象可能原因临时处理报错“文档安全令牌未正确形成”OnlyOffice 开了校验DzzOffice 没签或签错关闭 OnlyOffice token 校验或对齐 secret改 local.json 后仍报错配置未加载、JSON 格式错、多个实例检查日志确认 local.json 生效重启全部实例编辑器白屏无报错api.js 地址不对、跨域、端口不通浏览器 Network 看 api.js 是否 200能打开不能保存回调地址不可达、权限不足从 Document Server 访问回调 URL只有部分用户报错用户信息、时间、key 冲突检查用户 ID 和文件 key 是否唯一重启后短暂正常又报错缓存、负载均衡、配置回滚清缓存检查多节点配置一致性多语言界面不对editorConfig.lang 未设置在 DzzOffice 插件里指定语言Docker 部署改配置无效改在宿主机没进容器进容器改或挂载配置文件5.2 避免下次再踩的几条经验第一改配置前先备份改完先验证 JSON。第二secret 不要用肉眼比对复制粘贴后检查首尾空格。第三内网临时关闭校验后一定要记录在案设定恢复时间。第四DzzOffice 和 OnlyOffice 的地址尽量统一用域名不要一会儿内网 IP、一会儿外网域名。第五升级 OnlyOffice 或 DzzOffice 插件前先确认令牌配置是否会被覆盖。第六测试时用无痕窗口避免浏览器缓存骗你。还有一个很实用的经验如果你在用 SpringBoot 集成 OnlyOffice或者 Java 进行 onlyoffice在线编辑文书令牌生成逻辑最好抽成独立工具类secret 放配置中心不要散落在业务代码里。这样 DzzOffice 那边出问题时你可以快速比对两边的签名参数。onlyoffice多语言 场景下lang 字段也要放进 config别只改前端界面否则用户看到中英混杂。6. 如果必须保留令牌校验后续可以怎么修6.1 PHP 侧正确签发 JWT 的思路长期方案一定是开启令牌校验并且让 DzzOffice 正确签发 JWT。核心原则签名 payload 必须覆盖 Document Server 需要校验的 config 字段secret 双方一致算法一致时间有效。PHP 侧可以用firebase/php-jwt先生成完整 config再编码再把 token 塞回 config。不要先塞 token 再签名否则签名内容里包含 token 自身容易死循环。临时排障时可以先在 DzzOffice 里写一个测试脚本生成 token 后用 curl 请求 Document Server看返回什么。这样能把浏览器、前端缓存这些干扰因素排除掉。6.2 Java SpringBoot 集成时的注意点Java 侧常用java-jwt或jjwt。生成逻辑类似Algorithm algorithm Algorithm.HMAC256(secret); String token JWT.create() .withIssuedAt(new Date()) .withExpiresAt(new Date(System.currentTimeMillis() 3600_000)) .withClaim(document, documentMap) .withClaim(editorConfig, editorConfigMap) .sign(algorithm);注意 claim 的类型和 JSON 序列化结果要和 config 一致。Java 里 Map 转 JSON 后可能多出 null 字段或者数字变成字符串这些都可能影响校验。开发版连接器调试时最好把生成的 token 和 config 一起打印到日志但不要打印 secret。上线前把日志级别调回去避免敏感信息泄露。6.3 临时方案转长期方案的收尾动作临时关闭校验后文档能打开业务先恢复接下来要做三件事第一恢复 OnlyOffice 令牌校验设置一个强 secret第二DzzOffice 插件里填入同样的 secret清理缓存第三用普通用户、管理员、不同文件类型各测一遍打开、编辑、保存、协作。确认无误后把 local.json 备份更新记录变更时间和操作人。如果 Document Server 只在内部网络使用也建议在 Nginx 层加访问控制不要让它直接裸奔。令牌校验是应用层的一道门网络层再加一道心里更踏实。我个人在实际操作中的体会是遇到“文档安全令牌未正确形成”先别急着怀疑人生九成问题都在 secret、payload、时间、代理这四件事上。临时关闭校验能救急但救完急一定要回头把令牌链路修好。最后再分享一个小技巧把 DzzOffice 和 OnlyOffice 的配置各截一张图改完后对照检查比在脑子里记参数靠谱得多。
阅读完成 · 觉得有帮助?