概要
- SpringCloudGatewayでRequestBodyをいじってみるついでにjsonのフィールドをvalidateするGatewayFilterFactoryを作ってみた
- 名付けて
ValidateRequestBodyGatewayFilterFactory
!!(笑)
できること/制約
- フィールドに対して指定した正規表現を満たすかチェックできる
- 指定されていないフィールドを落とせる
- 多階層のjsonには対応していない
- 数値が文字列でバックポストされる("value":123 → "value":"123")
作ったもの
SpringCloudGatewayに用意されているModifyRequestBodyGatewayFilterFactory
を参考にしました
ValidateRequestBodyGatewayFilterFactory
@Component
public class ValidateRequestBodyGatewayFilterFactory extends AbstractGatewayFilterFactory<ValidateRequestBodyGatewayFilterFactory.Config> {
public ValidateRequestBodyGatewayFilterFactory() {
super(Config.class);
}
private final List<HttpMessageReader<?>> messageReaders = HandlerStrategies.withDefaults().messageReaders();
@Override
public GatewayFilter apply(Config config) {
return (exchange, chain) -> {
var serverRequest = ServerRequest.create(exchange, messageReaders);
var validatedBody = serverRequest.bodyToMono(StringMap.class)
.map(originalBody -> {
var verifiedParams = new StringMap();
for (var key: originalBody.keySet()) {
var pattern = config.getPattern(key);
if (pattern != null) {
var param = originalBody.get(key);
if (pattern.matcher(param).find()) {
verifiedParams.put(key, param);
}
}
}
return verifiedParams;
});
HttpHeaders headers = new HttpHeaders();
headers.putAll(exchange.getRequest().getHeaders());
headers.remove(HttpHeaders.CONTENT_LENGTH);
CachedBodyOutputMessage outputMessage = new CachedBodyOutputMessage(
exchange, headers);
BodyInserter bodyInserter = BodyInserters.fromPublisher(validatedBody, StringMap.class);
return bodyInserter.insert(outputMessage, new BodyInserterContext())
.then(Mono.defer(() -> {
var decorator = new ServerHttpRequestDecorator(exchange.getRequest()) {
@Override
public Flux<DataBuffer> getBody() {
return outputMessage.getBody();
}
@Override
public HttpHeaders getHeaders() {
return headers;
}
};
return chain.filter(exchange.mutate().request(decorator).build());
}));
};
}
@Getter
public static class Config {
private Map<String, Pattern> roles = new HashMap<>();
public void setRoles(String role) {
try {
Map<String, String> originalRoles = new ObjectMapper().readValue(role, StringMap.class);
for (var key: originalRoles.keySet()) {
this.roles.put(key, Pattern.compile(originalRoles.get(key)));
}
} catch (Exception ex) {
throw new IllegalArgumentException(ex);
}
}
public Pattern getPattern(String key) {
return this.roles.getOrDefault(key, null);
}
}
public static class StringMap extends HashMap<String, String> { }
}
使い方
application.yml
spring:
cloud:
gateway:
routes:
- id: validate
uri: <バックポスト先>
filters:
- name: ValidateRequestBody
args:
# jsonで「フィールド:正規表現」の組み合わせを定義する
roles: |
{"number":"^[0-9]+$"}
この設定だと
{"number":123,"hoge":"hoge"}
↓
{"number":"123"}
のようにnumberだけが透過する
何かの役に立つ時が来たらいいな😇