📹 第八章 ONVIF框架代码案例应用
📄 一句话概括

digest 的算法和操作系统一点关系都没有—— 所以「Windows 能过、Linux 过不了」一定不是算法差异。 真正的原因是三个:时钟不对、换行符混进密码、字符编码不一致。

📝 本节和前面几节的关系

08.01.05 讲「生成期埋结构」,08.04 讲「运行期怎么填」, 这一节讲「换到 Linux 之后为什么原来能过的过不了」。 它的价值不在新知识,在「排查方向」。

一、Linux 与 Windows 只差文件位置

项 Windows Linux
wsse.h 在哪 gSOAP 解压目录 /usr/local/gsoap/include
怎么让它被找到 拷到工程目录 -I 指过去
算法 一样 一样

第三行要说得绝对一点:WS-Security 是个规范, 它规定了 Base64(SHA1(nonce+created+password)) 这个算式,谁实现都得这么算。 所以「平台上过不了」这件事,逻辑上只能有三种原因: 输入不一样、环境不一样、代码路径不一样——而输入不一样是最常见的。

二、三个真正的原因

原因 表现 怎么确认
时钟差太多 Fault,理由「未授权」 date -u 和设备对一下
配置里带 \r 密码末尾多了个回车 repr() 打出来
字符编码不一致 密码里有中文/特殊符号 file 配置 看编码

第一行最容易被忽略:Created 是客户端按本机时间生成的, 而设备会用「自己的时间 ± 容差」去校验—— 如果客户端时钟差了十几分钟,设备会判定「这是过期请求」直接拒。 表现是「密码明明对,就是上不去」,而换个说法就是「设备的时间没同步」。 所以排查鉴权失败,第一步应该是对时,不是对密码。 第二行是 08.08.04 那个换行符的下游后果: 如果密码是从配置文件读的,而那个文件是 Windows 编辑过的——读出来的密码末尾就带着 \r。 第三行在「密码里有中文」时出现:文件是 GBK、代码按 UTF-8 读, 字节不一样,digest 当然不一样。

三、用 openssl 独立验证

排查这类问题最有效的手段是「绕开代码,用命令行算一遍」:

printf 'nonce原文Created密码' \
  | openssl dgst -sha1 -binary | base64

如果能算出和代码一样的值,说明算法没问题,问题在「发给设备的东西」; 如果算出来不一样,说明「你手里的输入和代码里的不一样」。 这个二分法能一步砍掉一半排查方向,比在代码里加一堆日志快得多。 命令里那串拼接顺序不能错:nonce 原文在前、Created 在中、密码在后。 顺序错了算出来是另一个值,而设备只会回「未授权」。 注意 printf 不是 echo:echo 会自动加换行, 而那个换行会被算进摘要里——所以用 echo 验证永远是错的。

四、跨平台项目该怎么防

措施 防什么 成本
配置文件只用 ASCII 编码问题 低
密码里不放中文 编码问题 低
统一用 UTC 时间 时钟问题 低
版本库里 text=auto 换行符 低

这四条共同点是「成本都很低」——它们不是「额外的安全措施」,而是「避免自找麻烦的约定」。 第一条那个「只用 ASCII」听着苛刻,但配置文件的 key 与 URI 本来就该是 ASCII, 只有注释可能有中文——而注释不参与解析。 第三条「统一用 UTC」在代码里就是 time.gmtime() 而不是 time.localtime(),一行之差, 但用错了就是「差 8 小时」(第七章 07.02.06 讲过这个坑)。 第四条是上一节的延续:text=auto 一次性解决「谁在哪个系统上编辑过」的问题, 而且不需要每个人记住「要跑 dos2unix」。

五、动手 Demo:鉴权算法不分平台

Demo 在本机算一遍 digest,给出一个「对照值」—— 这个值可以用 Linux 的 openssl 命令独立复算,两边一致就说明算法没错。 再验证「时钟差太多会怎样」:把 Created 改成昨天。

nonce = base64.b64encode(b"1234567890abcdef").decode()
created = "2026-10-24T12:00:00Z"
print(nonce, len(nonce))                  # 24 字符
print(S.digest(nonce, created), len(S.digest(nonce, created)))

# 用 openssl 独立复算(Linux)
# printf '1234567890abcdef2026-10-24T12:00:00Zadmin123' \
#   | openssl dgst -sha1 -binary | base64

实跑输出:

--------------------------------------------------------------------
鉴权算法不分平台
--------------------------------------------------------------------
**这一节标题是「鉴权(认证)」,但内容其实和 08.01.05 一模一样**:
**在 `typemap.dat` 里加 `SOAP_ENV__Header` 那一行,`wsdl2h` 就带鉴权结构了。**
**Linux 与 Windows 的差别只有 `wsse.h` 的位置**:

  项                 Windows            Linux
  -----------------------------------------------------------------
  `wsse.h` 在哪      gSOAP 解压目录     `/usr/local/gsoap/include`
  怎么让它被找到     拷到工程目录       **`-I` 指过去**
  **算法本身**       **一样**           **一样**

**第三行是重点:digest 的算法跟操作系统一点关系都没有**,
**它是 WS-Security 规范定义的三步,谁算都一样。**
**所以「Windows 上能过、Linux 上过不了」的问题,**
**几乎不可能是算法差异,只可能是**:
**① 时钟不对(Created 差几分钟,设备拒收)**、
**② 换行符混进密码(读了带 `\r` 的配置文件)**、
**③ 字符编码不一致(密码里有中文)。**

**本机算一遍,作为对照值**:

nonce 原文(16 字节)→ `MTIzNDU2Nzg5MGFiY2RlZg==`
Created → `2026-10-24T12:00:00Z`
digest → `SP8OVpfTu7lVpEpQV1X9SRH/6G0=`

**把这个三元组拿到 Linux 上用 `openssl` 算出同一个值**:

  `printf '1234567890abcdef2026-10-24T12:00:00Zadmin123' \
    | openssl dgst -sha1 -binary | base64`

**命令里那串不能写错顺序**:**nonce 原文在前,Created 在中,密码在后**。
**顺序错了算出来是另一个值,而设备只会回「未授权」,不会告诉你是顺序错了。**
**所以现场排查时,先拿这个命令验一遍,能对上再去看代码。**

输出里那个 28 字符的 digest 就是「对照值」: Linux 上用 openssl 算出来必须一模一样——一样就说明算法和输入都对,问题在别处。 第二段注释里的 printf 不能换成 echo:echo 会在末尾加一个换行符, 那个换行会被算进 SHA1,结果完全不同。 这个坑我第一次用时踩了很久。 注意命令里那串是「nonce 原文 + Created + 密码」直接拼接, 中间没有分隔符——这也是规范规定的,加任何分隔符都会算错。 所以「跨平台鉴权失败」的排查顺序应该是:先对时、再用这条命令验算法、最后才看代码。 反过来查会浪费很多时间。

30 秒实验

  1. 在 Linux 上用那条 openssl 命令算一遍,和这里的输出对比。
  2. 把 created 改成昨天的时间再算,看 digest 变不变。
  3. 把密码换成带中文的,看 base64 输出和预期差多少。

常见误区

  1. 以为「Linux 上鉴权过不了」是算法不同。 规范定死的算式,谁算都一样, 差别只可能在输入或环境。
  2. 用 echo 拼字符串去验证 digest。 echo 会加换行,算出来的值永远不对,要用 printf。
  3. 排查鉴权先查密码。 先对时——Created 差十几分钟,设备直接判「过期请求」。
自测三问(点开对答案) 1. 为什么说「平台上鉴权过不了」不可能是算法差异? —— **WS-Security 规定了固定算式**,任何平台实现都得一样。差别只能在输入、环境或代码路径。 2. 验证 digest 为什么必须用 `printf` 而不是 `echo`? —— **`echo` 末尾会加换行符**,那个换行会被算进 SHA1。`printf` 不加。 3. 排查鉴权失败的第一步应该是什么? —— **对时。** `Created` 是客户端按本机时间生成的,**差太多设备会判「过期请求」直接拒。**

下一节 soapcpp2:Linux 下多一个 -l,能省一半代码。

相关笔记