之前有这样一个问题。

 responseBody from  :{"data":{"payerId":"1952298788269985793","payerName":"刘一","payerType":"INDIVIDUAL","payerIdType":"ID_CARD","payerIdNo":"41xxx","status":"REJECT","failReason":"审核不通过:其他原因有误","createTime":"2025-08-04T09:21:18.000Z","modifyTime":"2025-08-04T09:21:18.000Z"},"code":"000000"} 10.0.0.105 2025-08-04 17:21:18,326 [http-nio-8003-exec-7] ERROR [m.g.modules.mybatis.controller.OrderController] OrderController.java:155 - error when call lianlian me.guanglian.exception.LianlianRequestException: 调用接口失败(返回的数据,签名验证不通过)。请求URL:/outbound/card/v1/payer响应内容:{"data":{"payerId":"1952298788269985793","payerName":"刘一","payerType":"INDIVIDUAL","payerIdType":"ID_CARD","payerIdNo":"41xxx","status":"REJECT","failReason":"审核不通过:其他原因有误","createTime":"2025-08-04T09:21:18.000Z","modifyTime":"2025-08-04T09:21:18.000Z"},"code":"000000"}

现在我切换成了当时出错的docker镜像,同样的请求,竟然不报验签失败了。

responseBody from :{"data":{"payerId":"1991772843112796161","payerName":"刘41xxx","payerType":"INDIVIDUAL","payerIdType":"ID_CARD","payerIdNo":"41xxx","status":"REJECT","failReason":"审核不通过:其他原因有误","createTime":"2025-11-21T07:37:06.000Z","modifyTime":"2025-11-21T07:37:06.000Z"},"code":"000000"} 10.0.0.105 2025-11-21 15:37:06,000 [http-nio-8003-exec-7] INFO [5f24b835c4724fd2ae6b3ae2a48ef066/e23c43427593d899]

这是出错时的dockerfile

# can not use jstack in this base image
FROM openjdk:8u322-jdk-bullseye

ENV LANG="en_US.UTF-8"
ENV TZ=Asia/Shanghai
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone

WORKDIR /data/app
COPY /target/st-remit.jar /data/app/st-remit-8003.jar
COPY target/classes/skywalking /data/app/agent
ENTRYPOINT ["/entrypoint"]
EXPOSE 8003
ENTRYPOINT ["java","-jar","-Dcom.sun.jndi.rmi.object.trustURLCodebase=false", "-Dcom.sun.jndi.cosnaming.object.trustURLCodebase=false","/data/app/st-remit-8003.jar","--spring.profiles.active"]

而使用如下就能正常

FROM openjdk:8u322-jdk-bullseye

ENV LANG="C.UTF-8"
ENV TZ=Asia/Shanghai

# 设置时区 + 安装字体(DejaVu Font)
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone \
    && apt-get update \
    && apt-get install -y --no-install-recommends fonts-dejavu fontconfig \
    && rm -rf /var/lib/apt/lists/*

WORKDIR /data/app

COPY /target/st-remit.jar /data/app/st-remit-8003.jar
#COPY target/classes/skywalking /data/app/agent

ENTRYPOINT ["java","-jar","-Dcom.sun.jndi.rmi.object.trustURLCodebase=false", "-Dcom.sun.jndi.cosnaming.object.trustURLCodebase=false","/data/app/st-remit-8003.jar"]
CMD ["--spring.profiles.active=prod"]

EXPOSE 8003

解释一下原因


你的旧 Dockerfile 会导致 验签所依赖的 JSON 串序列化格式不同(尤其是末尾的空格、行尾换行、编码等),从而导致验签失败;
而新 Dockerfile 环境(locale、字体、依赖库)让 JVM 使用了 不同的默认 Charset/Locale,从而使序列化结果一致,因此签名验证自然不再失败。

换句话说:

你的业务代码没变,变的是 Docker 镜像的系统环境,导致 JVM 的默认字符集、Locale、JSON 输出格式发生了改变 → 直接影响验签。

差异 1: Locale 不同(这是最核心的)

旧版本 Dockerfile
ENV LANG=”en_US.UTF-8”

新版本 Dockerfile
ENV LANG=”C.UTF-8”

Locale 影响
en_US.UTF-8 使用较完整的 UTF-8 处理,但受系统 locale 配置影响,有时会自动格式化数字、日期、字符串排序方式不同
C.UTF-8(POSIX) 严格稳定,不会根据地区改变格式,是 Docker 默认最可控、最可预测的 locale

支付的验签算法 对原文串要求极高:

换行

空格

字符顺序

UTF-8 编码

Unicode 归一化

任意一点不同,都导致签名不一致。

你旧镜像的 en_US.UTF-8 在某些系统里会自动进行:

Unicode normalization(NFC → NFD 或反之)

JSON 字符串的排序变化

数字/日期/小数点本地化

特殊字符自动替换

⚠️ 这些都会让“签名原文串”发生微小差异,导致验签失败。

而 C.UTF-8 是 Docker 中最稳定的 UTF-8,不会乱动内容,因此不再验签失败。

作者:张三  创建时间:2025-11-21 16:23
最后编辑:张三  更新时间:2025-11-28 13:34