Jackson ObjectMapper: specifica l’ordine di serializzazione delle proprietà dell’object

Sto implementando un servizio web RESTful in cui l’utente deve inviare un token di verifica firmato insieme alla richiesta in modo da poter garantire che la richiesta non sia stata manomessa da un intermediario. La mia attuale implementazione è la seguente.

Il token di verifica è un object VerifData serializzato in una stringa e quindi sottoposto a hash e crittografato.

class VerifData { int prop1; int prop2; } 

Nel mio servizio, ho inserito i dati da serializzare in un’istanza di VerifData e poi serializzato utilizzando Jackson ObjectMapper e trasferito al motore di verifica insieme al token di verifica.

 VerfiData verifData = new VerifData(12345, 67890); ObjectMapper mapper = new ObjectMapper(); String verifCodeGenerated = mapper.writeValueAsString(verifData); 

Ma sembra che ogni volta che viene avviato il contenitore dell’applicazione, l’ordine delle proprietà che vengono mappate in una stringa da ObjectMapper cambia.

Es: una volta sarebbe

 {"prop1":12345,"prop2":67890} 

e un’altra volta sarebbe

 {"prop2":67890,"prop1":12345} 

Quindi, se il client ha serializzato l’istanza di VerifData come nella prima stringa, c’è il 50% di possibilità che venga fallita anche se è corretta.

C’è un modo per aggirare questo? Posso specificare l’ordine delle proprietà da mappare con ObjectMapper (come in ordine crescente)? Oppure esiste un altro modo per implementare al meglio questa fase di verifica. Sia le implementazioni client e server sono sviluppate da me. Utilizzo l’API di sicurezza Java per la firma e la verifica.

Dalla documentazione di Jackson Annotations :

 // ensure that "id" and "name" are output before other properties @JsonPropertyOrder({ "id", "name" }) // order any properties that don't have explicit setting using alphabetic order @JsonPropertyOrder(alphabetic=true) 

Le annotazioni sono utili, ma possono essere dolorose da applicare ovunque. Puoi configurare l’intero ObjectMapper in modo che funzioni in questo modo

Versioni attuali di Jackson: objectMapper.configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true)

Versioni precedenti di Jackson: objectMapper.configure(SerializationConfig.Feature.SORT_PROPERTIES_ALPHABETICALLY, true);

In Spring Boot è ansible aggiungere questo comportamento a livello globale aggiungendo quanto segue alla class del punto di ingresso Application :

  @Bean public Jackson2ObjectMapperBuilder objectMapperBuilder() { Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder(); builder.featuresToEnable(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY); return builder; } 

C’è un modo più semplice in Spring Boot specificando una proprietà (in application.properties ad esempio:

 spring.jackson.mapper.sort_properties_alphabetically=true 

Dalla risposta di Duncan McGregor: è meglio usarla in questo modo:

 objectMapper.configure(SerializationConfig.Feature.SORT_PROPERTIES_ALPHABETICALLY, true); 

come MapperFeature è per XML e viene fornito con jackson-databind che non è richiesto …

In Jackson 2.x, che probabilmente stai usando oggi, usa:

 ObjectMapper mapper = new ObjectMapper(); mapper.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, true); 

Se ti interessano gli aspetti, potresti anche considerare SerializationFeature.INDENT_OUTPUT .

Notare che è necessario serializzare Mappe o Oggetti per ordinare correttamente. Se serializzi un JsonNode per esempio (da readTree ), questo non sarà correttamente rientrato.

Esempio

 import com.fasterxml.jackson.databind.*; ObjectMapper mapper = new ObjectMapper(); mapper.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, true); mapper.configure(SerializationFeature.INDENT_OUTPUT, true); String input = "{\"hello\": {\"cruel\" : \"world\"} }"; Object pojo = mapper.readValue(input, Object.class); System.out.println(mapper.writeValueAsString(pojo)); 

risultati in:

 { "hello" : { "cruel" : "world" } }