RESTful Webサービスを実装しています。ユーザーは、署名された検証トークンをリクエストとともに送信する必要があります。これにより、リクエストが仲介者によって改ざんされていないことを確認できます。私の現在の実装は次のとおりです。
検証トークンは、文字列にシリアル化され、ハッシュおよび暗号化されたVerifDataオブジェクトです。
class VerifData {
int prop1;
int prop2;
}
私のサービスでは、シリアル化するデータをVerifDataのインスタンスに入れ、Jackson ObjectMapperを使用してシリアル化し、検証トークンとともに検証エンジンに渡しました。
VerfiData verifData = new VerifData(12345, 67890);
ObjectMapper mapper = new ObjectMapper();
String verifCodeGenerated = mapper.writeValueAsString(verifData);
ただし、アプリケーションコンテナが起動されるたびに、ObjectMapperによって文字列にマッピングされるプロパティの順序が変わるようです。
例:1回
{"prop1":12345,"prop2":67890}
そして別の時間になるだろう
{"prop2":67890,"prop1":12345}
したがって、クライアントがVerifDataインスタンスを最初の文字列にシリアル化した場合、正しいにもかかわらず、失敗する可能性は50%です。
これを回避する方法はありますか? ObjectMapperによってマッピングするプロパティの順序(昇順など)を指定できますか?または、この検証手順を最適に実装する他の方法はありますか。クライアントとサーバーの両方の実装は私によって開発されました。署名と検証にJava Security APIを使用します。
Jackson Annotations documentation から:
// 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)
注釈は便利ですが、どこにでも適用するのは面倒です。このように動作するようにObjectMapper全体を設定できます
ジャクソンの現在のバージョン:objectMapper.configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true)
古いバージョンのジャクソン:objectMapper.configure(SerializationConfig.Feature.SORT_PROPERTIES_ALPHABETICALLY, true);
Spring Bootでは、Application
エントリポイントクラスに以下を追加することにより、この動作をグローバルに追加できます。
@Bean
public Jackson2ObjectMapperBuilder objectMapperBuilder() {
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
builder.featuresToEnable(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY);
return builder;
}
Spring Bootでは、プロパティを指定することで簡単な方法があります(例:application.properties
で:
spring.jackson.mapper.sort_properties_alphabetically=true
おそらく今日使用しているジャクソン2.xでは、次を使用します。
ObjectMapper mapper = new ObjectMapper();
mapper.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, true);
見た目が気になる場合は、SerializationFeature.INDENT_OUTPUT
も考慮する必要があります。
正しくソートするには、MapsまたはObjectsをシリアル化する必要があることに注意してください。たとえば、JsonNode
をシリアル化すると(readTree
から)、適切にインデントされません。
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));
結果:
{
"hello" : {
"cruel" : "world"
}
}
Duncan McGregor's answer:このように使用する方が良い:
objectMapper.configure(SerializationConfig.Feature.SORT_PROPERTIES_ALPHABETICALLY, true);
mapperFeatureはXML用であり、必須ではないjackson-databindが付属しているため...
フラグ引数を使用する代わりに:
objectMapper.enable(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY);