1. 程式人生 > 程式設計 >解決fastjson從1.1.41升級到1.2.28後報錯問題詳解

解決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的相關文章請點選下面的相關連結