Wdrażam usługę sieciową RESTful, w której użytkownik musi wysłać podpisany token weryfikacyjny wraz z żądaniem, aby upewnić się, że żądanie nie zostało zmodyfikowane przez pośrednika. Moja obecna realizacja jest następująca.
Token weryfikacyjny to obiekt VerifData serializowany w łańcuch, a następnie zaszyfrowany i zaszyfrowany.
class VerifData {
int prop1;
int prop2;
}
W mojej usłudze umieszczam dane do serializacji w instancji VerifData, a następnie serializuję je za pomocą Jackson ObjectMapper i przesyłam do silnika weryfikacji wraz z tokenem weryfikacyjnym.
VerfiData verifData = new VerifData(12345, 67890);
ObjectMapper mapper = new ObjectMapper();
String verifCodeGenerated = mapper.writeValueAsString(verifData);
Wydaje się jednak, że za każdym razem, gdy uruchamiany jest kontener aplikacji, kolejność właściwości mapowanych na ciąg znaków przez ObjectMapper zmienia się.
Np .: kiedyś to będzie
{"prop1":12345,"prop2":67890}
i innym razem będzie
{"prop2":67890,"prop1":12345}
Jeśli więc klient zserializował wystąpienie VerifData w pierwszym ciągu, istnieje 50% szans na niepowodzenie, mimo że jest poprawne.
Czy można to obejść? Czy mogę określić kolejność właściwości do mapowania przez ObjectMapper (jak w porządku rosnącym)? Czy jest inny sposób, aby najlepiej wdrożyć ten krok weryfikacji. Zarówno implementacje klienta, jak i serwera są opracowywane przeze mnie. Do podpisywania i weryfikacji używam Java Security API.
źródło
Adnotacje są przydatne, ale ich zastosowanie wszędzie może być trudne. Możesz skonfigurować cały ObjectMapper do pracy w ten sposób
Aktualne wersje Jacksona:
objectMapper.configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true)
Starsze wersje Jacksona:
objectMapper.configure(SerializationConfig.Feature.SORT_PROPERTIES_ALPHABETICALLY, true);
źródło
W Jackson 2.x, którego prawdopodobnie używasz dzisiaj, użyj:
ObjectMapper mapper = new ObjectMapper(); mapper.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, true);
Jeśli zależy Ci na wyglądzie, możesz również rozważyć
SerializationFeature.INDENT_OUTPUT
.Zauważ, że musisz serializować mapy lub obiekty, aby to posortowało poprawnie. Jeśli
JsonNode
na przykład serializujesz (fromreadTree
), nie będzie to odpowiednio wcięte.Przykład
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));
prowadzi do:
{ "hello" : { "cruel" : "world" } }
źródło
W Spring Boot możesz dodać to zachowanie globalnie, dodając następujące elementy do swojej
Application
klasy punktu wejścia:@Bean public Jackson2ObjectMapperBuilder objectMapperBuilder() { Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder(); builder.featuresToEnable(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY); return builder; }
źródło
W Spring Boot jest łatwiejszy sposób poprzez określenie właściwości (
application.properties
na przykład:spring.jackson.mapper.sort_properties_alphabetically=true
źródło
Następujące 2 konfiguracje ObjectMapper:
ObjectMapper.configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true)
lub
ObjectMapper.enable(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY)
i
ObjectMapper.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, true)
lub
ObjectMapper.enable(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS)
Przykład konfiguracji Spring Boot (yaml):
spring: jackson: mapper: SORT_PROPERTIES_ALPHABETICALLY: true serialization: ORDER_MAP_ENTRIES_BY_KEYS: true
źródło
Z odpowiedzi Duncana McGregora: lepiej go używać w ten sposób:
objectMapper.configure(SerializationConfig.Feature.SORT_PROPERTIES_ALPHABETICALLY, true);
ponieważ MapperFeature jest przeznaczony dla XML i jest dostarczany z jackson-databind, który nie jest wymagany ...
źródło
Zamiast używać argumentu flag:
źródło
Możesz użyć mieszania i określić kolejność właściwości, jak chcesz:
import com.fasterxml.jackson.annotation.JsonPropertyOrder; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.context.annotation.Bean; import org.springframework.stereotype.Component; @Component public final class ObjectMapperUtils { private static final ObjectMapper MAPPER = new ObjectMapper(); static { MAPPER.addMixIn(Object.class, IdFirst.class); } @Bean public ObjectMapper objectMapper() { return MAPPER; } @JsonPropertyOrder({"id", "...", "..."}) private abstract static class IdFirst {} }
źródło
Zdaję sobie sprawę, że to stary wątek, ale ponieważ szukałem odpowiedzi i wylądowałem tutaj, dodatkowe informacje mogą być przydatne dla innych osób.
Adnotacja @JsonProperty, której obecnie używam (jackson-annotations-2.11.2) akceptuje, oprócz argumentu „wartość”, argument „indeks” (numeryczny), który określa kolejność pól podczas serializacji.
źródło