Java 项目对接 Python 服务端接口时,需要做参数 md5 校验
Python 中处理参数是通过 json.dumps() 方式先拿到请求 json 再计算 md5 值的
但是 Python 和 Java 序列化的结果不一致,导致 md5 验证无法通过
Python 代码示例
#!/usr/bin/env python3
# coding: utf-8
from json import dumps
if __name__ == '__main__':
demo_bean = {
"id": 1,
"name": "demoName",
"values": [1, 2, 3, 4]
}
demo_json = dumps(demo_bean, sort_keys=True).encode('utf-8')
print(demo_json)
// 输出结果:
// b'{"id": 1, "name": "demoName", "values": [1, 2, 3, 4]}'
Java 我用的是 FastJson
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.google.common.collect.Lists;
import lombok.Builder;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import java.util.List;
@Slf4j
public class FastJsonTest {
@Test
public void testClean() throws Exception {
DemoBean demoBean = DemoBean.builder()
.id(1)
.name("demoName")
.values(Lists.newArrayList(1, 2, 3, 4))
.build();
String demoJson = JSON.toJSONString(demoBean, SerializerFeature.SortField);
log.debug("demoJson={}", demoJson);
}
}
@Data
@Builder
class DemoBean {
private Integer id;
private String name;
private List<Integer> values;
}
// 输出
// demoJson={"id":1,"name":"demoName","values":[1,2,3,4]}
我比较了下两种方式的输出,主要是 Python 序列化结果多了一些 “空格”。
python: {"id": 1, "name": "demoName", "values": [1, 2, 3, 4]}
java : {"id":1,"name":"demoName","values":[1,2,3,4]}
目前 Python 服务端代码不能变更,只能使 Java 的 json 对象序列化结果和 Python 的一致,有什么好的方式吗?
搞定,下班 😁
重写了 jackson 的 MinimalPrettyPrinter 类,具体看下面:
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.fasterxml.jackson.core.JsonGenerationException;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.util.MinimalPrettyPrinter;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.Lists;
...
@Slf4j
public class FastJsonTest {
@Test
public void testClean() throws Exception {
DemoBean demoBean = DemoBean.builder()
.id(1)
.name("demoName")
.values(Lists.newArrayList(1, 2, 3, 4))
.build();
String demoJson = JSON.toJSONString(demoBean, SerializerFeature.SortField);
log.debug("demoJson={}", demoJson);
// {"id":1,"name":"demoName","values":[1,2,3,4]}
String demoJson2 = new ObjectMapper().writer(new MyPrettyPrinter()).writeValueAsString(demoBean);
log.debug("demoJson2={}", demoJson2);
// {"id": 1, "name": "demoName", "values": [1, 2, 3, 4]}
}
}
@Data
@Builder
class DemoBean {
private Integer id;
private String name;
private List<Integer> values;
}
class MyPrettyPrinter extends MinimalPrettyPrinter {
@Override
public void writeObjectFieldValueSeparator(JsonGenerator jg) throws IOException {
jg.writeRaw(':');
jg.writeRaw(' ');
}
@Override
public void writeObjectEntrySeparator(JsonGenerator jg) throws IOException, JsonGenerationException {
jg.writeRaw(',');
jg.writeRaw(' ');
}
@Override
public void writeArrayValueSeparator(JsonGenerator jg) throws IOException, JsonGenerationException {
jg.writeRaw(',');
jg.writeRaw(' ');
}
}
谢谢大家的建议了 🙏
原来的总结还忽略了中文的情况,趁现在可以摸鱼,汇总了下,希望大家用不上
1
linhua 2018-10-23 14:30:25 +08:00
https://stackoverflow.com/questions/16311562/python-json-without-whitespaces
https://docs.python.org/3/library/json.html#json.dump If specified, separators should be an (item_separator, key_separator) tuple. The default is (', ', ': ') if indent is None and (',', ': ') otherwise. To get the most compact JSON representation, you should specify (',', ':') to eliminate whitespace. 手动将', '和': '替换成',',':' |
2
myyou 2018-10-23 14:31:27 +08:00
json.dumps(demo_bean, sort_keys=True, separators=(',', ':'))
|
3
zacharyjia 2018-10-23 14:36:42 +08:00
楼上两位没好好看题呀,楼主说 Python 部分不能改了,只能想办法在 Java 里加了
|
4
misaka19000 2018-10-23 14:40:28 +08:00
改算法,JSON 类型的数据不应该因为空格就导致数据的 hash 结果不一致,更好的办法是根据 key 和 value 的值来计算 hash 的值
|
5
whileFalse 2018-10-23 14:49:32 +08:00 via iPhone
竟然不是排序 key 之后根据 key 和 value 自己写 hash …
|
6
xmt328 2018-10-23 16:10:03 +08:00
这个设计好有问题啊,环境依赖的这么严重,谁设计的不怕被打么
|
7
corningsun OP @misaka19000 @whileFalse
是的,但是 Python 服务端现状就是这个样子了,没法让对方改了。 已经把 FastJson 源码看了一遍了,并没有找到设置“空格”的地方。。😢 ```java package com.alibaba.fastjson.serializer; public class FieldSerializer implements Comparable<FieldSerializer> { private final String double_quoted_fieldPrefix; private String single_quoted_fieldPrefix; public FieldSerializer(Class<?> beanType, FieldInfo fieldInfo){ ... this.double_quoted_fieldPrefix = '"' + fieldInfo.name + "\":"; ... } public void writePrefix(JSONSerializer serializer) throws IOException { SerializeWriter out = serializer.out; if (out.quoteFieldNames) { if (out.useSingleQuotes) { if (single_quoted_fieldPrefix == null) { single_quoted_fieldPrefix = '\'' + fieldInfo.name + "\':"; } out.write(single_quoted_fieldPrefix); } else { out.write(double_quoted_fieldPrefix); } } else { if (un_quoted_fieldPrefix == null) { this.un_quoted_fieldPrefix = fieldInfo.name + ":"; } out.write(un_quoted_fieldPrefix); } } ``` |
8
misaka19000 2018-10-23 16:15:04 +08:00
@corningsun #7 既然是这样那么比较挫的办法就是把所有的 ',' replace 为', ' 了
|
9
corningsun OP @misaka19000
现在就是这么干的,但是有个字段是富文本,很容易把别的内容覆盖掉,所以来找更好的方法。 |
10
PulpFunction 2018-10-23 16:44:56 +08:00
上正则
或者在值后面加个 logo 比如{"id":1@#*,"name":"demoName@#*","values":[1@#*,2@#*,3@#*,4@#*]} 接着再用 1 楼的方案 |
11
PulpFunction 2018-10-23 16:46:13 +08:00
再富的文本也不能富过 logo 吧
嫌短再加长点 |
12
PulpFunction 2018-10-23 16:47:02 +08:00
我觉得 3 个###就行
|
13
PulpFunction 2018-10-23 16:48:42 +08:00
还有细节问题
|
14
PulpFunction 2018-10-23 16:54:11 +08:00
{"id":***1###,"name":"***demoName###","values":***[1###,2###,3###,4]}
|
15
PulpFunction 2018-10-23 16:55:12 +08:00
采纳记得发送感谢,每一个都发送啊,币不多了
|
16
kkkkkrua 2018-10-23 17:07:32 +08:00
String value = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(demoApplication);
System.out.println(value.replace("\r\n","").replace("{ ","{")); //最后的首{可以用正则匹配下中间的空格 |
17
kkkkkrua 2018-10-23 17:08:52 +08:00
思路是先美化输出,然后替换掉换行,再替换开头的{中间的空格
//美化输出的 { "name" : "张三", "age" : 18 } //替换后的 {"name" : "张三", "age" : 18} |
18
woodensail 2018-10-23 17:09:56 +08:00
不能拿到原始的 requestbody 直接进行 hash 吗?
|
19
kkkkkrua 2018-10-23 17:10:36 +08:00
忘记说了,包是 com.fasterxml.jackson.databind.ObjectMapper;不知道 fastjson 有没有美化方法
|
20
woodensail 2018-10-23 17:23:54 +08:00
或者可以这么干。请求就带俩参数,一个是 md5 一个是 data。data 就是 json 字符串。服务端拿到后直接对字符串进行 md5 校验,校验通过了对 data 进行解析得到真实参数。
|
21
corningsun OP @woodensail 服务端代码改不了了。
@kkkkkrua fastjson 也有 pretty 方法,但是只是增加了 换行,没有加空格。 objectMapper 的 pretty 方法,在冒号的 两边都加了空格,只去除换行还不够。另外处理 list 数组时,也不一致。 |
22
corningsun OP 终于找到方法了,具体看附言内容,谢谢大家了。
|
23
Sum0l 2023-09-11 14:24:40 +08:00
学习了,还得是该源码最可靠 哈哈
|