解決fastjson從1.1.41升級到1.2.28後報錯問題詳解
最近因為fastjson安全漏洞,升級jar包時,踩了一些坑。
新版本FastJsonHttpMessageConverter初始化,預設設定MediaType為*/*
背景:
使用Spring RestTemplate,配置如下:
<bean id="restTemplate" class="org.springframework.web.client.RestTemplate"> <constructor-arg ref="ky.clientHttpRequestFactory"/> <property name="errorHandler"> <bean class="org.springframework.web.client.DefaultResponseErrorHandler"/> </property> <property name="messageConverters"> <list> <bean class="org.springframework.http.converter.FormHttpMessageConverter"/> <bean class="cn.com.autodx.common.jsonView.ViewAwareJsonMessageConverter"> </bean> <bean class="org.springframework.http.converter.StringHttpMessageConverter"> <property name="supportedMediaTypes"> <list> <value>text/html;charset=UTF-8</value> <value>application/json</value> <value>text/javascript;charset=utf-8</value> </list> </property> </bean> </list> </property> </bean>
其中ViewAwareJsonMessageConverter繼承自FastJsonHttpMessageConverter。
fastjson從1.1.41升級到1.2.28之後,請求報錯:
json java.lang.IllegalArgumentException: 'Content-Type' cannot contain wildcard type '*'
原因是在1.1.41中,FastJsonHttpMessageConverter初始化時,設定了MediaType。
public FastJsonHttpMessageConverter(){ super(new MediaType("application","json",UTF8),new MediaType("application","*+json",UTF8)); }
而在1.2.28中,設定的MediaType為‘/',即:
public FastJsonHttpMessageConverter() { super(MediaType.ALL); // */* }
後續在org.springframework.http.converter.AbstractHttpMessageConverter.write過程中,又要判斷Content-Type不能含有萬用字元,這應該是一種保護機制,並強制使用者自己配置MediaType。程式碼如下:
@Override public final void write(final T t,MediaType contentType,HttpOutputMessage outputMessage) throws IOException,HttpMessageNotWritableException { final HttpHeaders headers = outputMessage.getHeaders(); if (headers.getContentType() == null) { MediaType contentTypeToUse = contentType; if (contentType == null || contentType.isWildcardType() || contentType.isWildcardSubtype()) { contentTypeToUse = getDefaultContentType(t); } if (contentTypeToUse != null) { //設定Content-Type,不允許含有萬用字元 headers.setContentType(contentTypeToUse); } } ...... if (outputMessage instanceof StreamingHttpOutputMessage) { ...... }else { //自定義MessageConverter的write操作 writeInternal(t,outputMessage); outputMessage.getBody().flush(); } } public void setContentType(MediaType mediaType) { Assert.isTrue(!mediaType.isWildcardType(),"'Content-Type' cannot contain wildcard type '*'"); Assert.isTrue(!mediaType.isWildcardSubtype(),"'Content-Type' cannot contain wildcard subtype '*'"); set(CONTENT_TYPE,mediaType.toString()); }
所以,需要為ViewAwareJsonMessageConverter設定supportedMediaTypes:
<bean class="cn.com.autodx.common.jsonView.ViewAwareJsonMessageConverter"> <property name="supportedMediaTypes"> <list> <value>application/json;charset=UTF-8</value> <value>application/*+json;charset=UTF-8</value> </list> </property> </bean>
新版本序列化預設不再對欄位進行排序
這個是一個簽名演算法的場景:客戶端對引數進行序列化,然後md5加密成一個簽名;服務端按照相同的演算法解析一遍引數,對比簽名值。這裡加密依賴json序列化之後的字串,也就依賴序列化時欄位的排序。
這是fastjson做了一個性能優化,將排序需求抽象出一個SerializerFeature,供使用者自己配置。如果需要排序場景,在序列化時新增引數SerializerFeature.MapSortField即可,即:
JSON.toJSONString(obj,SerializerFeature.MapSortField);
官方文件
1.2.3之後的版本,Map的序列化沒有做排序再輸出,原因是通過TreeMap排序很影響效能。
1.2.27版本中增加SerializerFeature.MapSortField實現同樣的功能。
使用方法如下:
a) 傳入SerializerFeature.MapSortField引數。 JSON.toJSONString(map,SerializerFeature.MapSortField);
b) 通過程式碼修改全域性預設配置。 JSON.DEFAULT_GENERATE_FEATURE |= SerializerFeature.MapSortField.getMask();
c) 通過JVM啟動引數配置修改全域性配置 -Dfastjson.serializerFeatures.MapSortField=true
d) 通過類路徑下的fastjson.properties來配置 fastjson.serializerFeatures.MapSortField=true
新老版本序列化和反序列化不相容,會出亂碼。
更多關於fastjson的相關文章請點選下面的相關連結