Estoy implementando un servicio web RESTful donde el usuario tiene que enviar un token de verificación firmado junto con la solicitud para poder asegurarme de que la solicitud no ha sido manipulada por un intermediario. Mi implementación actual es la siguiente.
El token de verificación es un objeto VerifData serializado en una cadena y luego codificado y cifrado.
class VerifData {
int prop1;
int prop2;
}
En mi servicio, coloco los datos para serializarlos en una instancia de VerifData y luego los serializo usando Jackson ObjectMapper y los paso al motor de verificación junto con el token de verificación.
VerfiData verifData = new VerifData(12345, 67890);
ObjectMapper mapper = new ObjectMapper();
String verifCodeGenerated = mapper.writeValueAsString(verifData);
Pero parece que cada vez que se inicia el contenedor de la aplicación, cambia el orden de las propiedades que ObjectMapper asigna a una cadena.
Ej: una vez sería
{"prop1":12345,"prop2":67890}
y en otra ocasión sería
{"prop2":67890,"prop1":12345}
Entonces, si el cliente ha serializado la instancia VerifData como en la primera cadena, hay un 50% de posibilidades de que falle aunque sea correcta.
¿Hay alguna forma de evitar esto? ¿Puedo especificar el orden de las propiedades para mapear por ObjectMapper (como en orden ascendente)? ¿O hay alguna otra forma de implementar mejor este paso de verificación? Tanto las implementaciones de cliente como de servidor las desarrollo yo. Utilizo la API de seguridad de Java para firmar y verificar.
fuente
Las anotaciones son útiles, pero puede resultar complicado aplicarlas en todas partes. Puede configurar todo su ObjectMapper para que funcione de esta manera con
Versiones actuales de Jackson:
objectMapper.configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true)
Versiones anteriores de Jackson:
objectMapper.configure(SerializationConfig.Feature.SORT_PROPERTIES_ALPHABETICALLY, true);
fuente
En Jackson 2.x, que probablemente esté usando hoy, use:
ObjectMapper mapper = new ObjectMapper(); mapper.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, true);
Si te importa la apariencia, también puedes considerarlo
SerializationFeature.INDENT_OUTPUT
.Tenga en cuenta que debe serializar mapas u objetos para que esto se ordene correctamente. Si serializa un
JsonNode
por ejemplo (fromreadTree
), no se sangrará correctamente.Ejemplo
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));
resulta en:
{ "hello" : { "cruel" : "world" } }
fuente
En Spring Boot, puede agregar este comportamiento globalmente agregando lo siguiente a su
Application
clase de punto de entrada:@Bean public Jackson2ObjectMapperBuilder objectMapperBuilder() { Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder(); builder.featuresToEnable(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY); return builder; }
fuente
Hay una forma más fácil en Spring Boot especificando una propiedad (en,
application.properties
por ejemplo:spring.jackson.mapper.sort_properties_alphabetically=true
fuente
Se requiere la siguiente configuración de 2 ObjectMapper:
ObjectMapper.configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true)
o
ObjectMapper.enable(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY)
y
ObjectMapper.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, true)
o
ObjectMapper.enable(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS)
Ejemplo de configuración de Spring Boot (yaml):
spring: jackson: mapper: SORT_PROPERTIES_ALPHABETICALLY: true serialization: ORDER_MAP_ENTRIES_BY_KEYS: true
fuente
De la respuesta de Duncan McGregor: Es mejor usarlo así:
objectMapper.configure(SerializationConfig.Feature.SORT_PROPERTIES_ALPHABETICALLY, true);
ya que MapperFeature es para XML y viene con jackson-databind que no es necesario ...
fuente
En lugar de usar el argumento de la bandera:
fuente
Puede utilizar la combinación y especificar el orden de las propiedades como desee:
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 {} }
fuente
Me doy cuenta de que este es un hilo antiguo, pero como estaba buscando una respuesta y llegué aquí, alguna información adicional podría ser útil para otras personas.
La anotación @JsonProperty que estoy usando actualmente (jackson-annotations-2.11.2) acepta, además del argumento "valor", un argumento "índice" (numérico) que especifica el orden de los campos durante la serialización.
fuente