-
前提场景:使用OpenFeign访问一个外部接口,访问时需要将请求体的参数序列化为字符串然后加密等生成sign放到请求头中。
-
现象:访问时出现签名验证失败;
-
分析:其实请求体和在签名中的请求参数是一样的,但是在生成签名时我使用的fastjson,而OpenFeign默认的序列化器是Jackson。虽然从外部结果上看来序列化结果都一样,但是如果放在postman中测试,就会发现:
两者字符串相同,但是格式化后的请求体参数访问失败,而未格式化的请求体访问成功。 -
原因:在生成签名时使用的fastjson的Json.toJsonString(),他默认是不会格式化json字符串的,所以当服务拿到sign时解析发现是未格式化的json字符串,而请求体是OpenFeign使用Jackson格式化的json,所以两者对比失败,就请求失败。
而使用postman测试时,请求体是未格式化的json,sign当中也是未格式化的json,所以可以请求成功。 -
解决方式:在代码中,在sign生成过程中和OpenFeign访问时使用相同的序列化和格式化方式可以保证请求成功。因为我的项目都是用的FastJson作为序列化器,所以把请求体参数用于生成sign时,使用
JSON.toJSONString(param, JSONWriter.Feature.PrettyFormat, JSONWriter.Feature.NotWriteEmptyArray)
格式化,并且修改了feign接口默认的序列化器
@Bean
public Encoder feignEncoder() {
return new SpringEncoder(feignHttpMessageConverter());
}
private ObjectFactory feignHttpMessageConverter() {
HttpMessageConverters httpMessageConverters = new HttpMessageConverters(getFastJsonConverter());
return () -> httpMessageConverters;
}
private FastJsonHttpMessageConverter getFastJsonConverter() {
FastJsonHttpMessageConverter fastJsonHttpMessageConverter = new FastJsonHttpMessageConverter();
// -------------------设置MediaType 没有这里会报错 Content-type 通配符问题
List<MediaType> supportedMediaTypes = new ArrayList<>();
supportedMediaTypes.add(MediaType.APPLICATION_JSON);
supportedMediaTypes.add(MediaType.APPLICATION_ATOM_XML);
supportedMediaTypes.add(MediaType.APPLICATION_FORM_URLENCODED);
supportedMediaTypes.add(MediaType.APPLICATION_OCTET_STREAM);
supportedMediaTypes.add(MediaType.APPLICATION_PDF);
supportedMediaTypes.add(MediaType.APPLICATION_RSS_XML);
supportedMediaTypes.add(MediaType.APPLICATION_XHTML_XML);
supportedMediaTypes.add(MediaType.APPLICATION_XML);
supportedMediaTypes.add(MediaType.IMAGE_GIF);
supportedMediaTypes.add(MediaType.IMAGE_JPEG);
supportedMediaTypes.add(MediaType.IMAGE_PNG);
supportedMediaTypes.add(MediaType.TEXT_EVENT_STREAM);
supportedMediaTypes.add(MediaType.TEXT_HTML);
supportedMediaTypes.add(MediaType.TEXT_MARKDOWN);
supportedMediaTypes.add(MediaType.TEXT_PLAIN);
supportedMediaTypes.add(MediaType.TEXT_XML);
fastJsonHttpMessageConverter.setSupportedMediaTypes(supportedMediaTypes);
//修改配置返回内容的过滤
FastJsonConfig config = new FastJsonConfig();
config.setDateFormat("yyyy-MM-dd HH:mm:ss");
config.setReaderFeatures(JSONReader.Feature.FieldBased, JSONReader.Feature.SupportArrayToBean);
config.setWriterFeatures(JSONWriter.Feature.PrettyFormat, JSONWriter.Feature.NotWriteEmptyArray);
fastJsonHttpMessageConverter.setFastJsonConfig(config);
return fastJsonHttpMessageConverter;
}