[Spring] 커스텀 애노테이션(Custom Annotation)으로 마스킹 처리

0

Annotation Driven Masking (With. Jackson Library)

Custom Annotation 생성

Custom Annotation Type 정의

public enum MaskingType {
    NAME,
    CONTACT,
    EMAIL,
    CREDIT_CARD,
    BIRTHDAY
}

Custom Annotation 정의

@Target({ElementType.ANNOTATION_TYPE, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@JsonSerialize(using = StringPropertyMasker.class)
public @interface Masked {

    MaskingType type();

}

마스킹 메서드 구현

public class Masking {

    public static String mask(MaskingType type, String value) {
        if (type == null) return value;
        

        String str = "";
        switch (type) {
            case NAME:
                str = NameMaskOf(value);
                break;
            case CONTACT:
                str = ContactMaskOf(value);
                break;
            case BIRTHDAY:
                str = BirthDayMaskOf(value);
                break;
            default:
                str = value;
                break;
        }
        return str;
    }
}

Custom Serializer 구현

public class StringPropertyMasker extends StdSerializer<String> implements ContextualSerializer {
    private MaskingType maskingType;

    protected StringPropertyMasker() {
        super(String.class);
    }

    protected StringPropertyMasker(MaskingType maskingType) {
        super(String.class);
        this.maskingType = maskingType;
    }

    @Override
    public void serialize(String value, JsonGenerator gen, SerializerProvider provider) throws IOException {
        gen.writeString(Masking.mask(maskingType, value));
    }

    @Override
    public JsonSerializer<?> createContextual(SerializerProvider serializerProvider, BeanProperty property) {
        if (property != null) {
            Masked masked = property.getAnnotation(Masked.class);
            if (masked != null) {
                // 필드에 @Masked 애노테이션이 있는 경우, 애노테이션의 값에 따라 적절한 마스킹 로직을 선택하여 반환합니다.
                return new StringPropertyMasker(masked.type());
            }
        }
        return this;
    }
}

Custom Serializer Bean 주입

  1. java config 방식
// java Config
@Configuration
public class CustomObjectMapper {

    @Bean
    public ObjectMapper objectMapper() {
        ObjectMapper objectMapper = new ObjectMapper();
        
        SimpleModule module = new SimpleModule();
        module.addSerializer(String.class, new StringPropertyMasker());
        objectMapper.registerModule(module);
        
        return objectMapper;
    }
}
  1. xml config 방식
<!-- xml config -->
<bean id="jacksonObjectMapper" class="me.choicore.config.CustomObjectMapper"/>
<!-- Usage 1. -->
<mvc:annotation-driven>
	<mvc:message-converters>
      
      <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
        <property name="objectMapper" ref="jacksonObjectMapper"/>
        <property name="supportedMediaTypes">
          <list>
            <value>application/json;charset=UTF-8</value>
          </list>
        </property>
      </bean>
	</mvc:message-converters>
</mvc:annotation-driven>

<!-- Usage 2. -->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
  <property name="messageConverters">
    <list>
      <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
        <property name="objectMapper" ref="jacksonObjectMapper"/>
        <property name="supportedMediaTypes">
          <list>
            <value>application/json;charset=UTF-8</value>
          </list>
        </property>
      </bean>
    </list>
  </property>
</bean>

사용 방법

public class Account {

	@Masked(type = MaskingType.NAME)
	private String username;

	@Masked(type = MaskingType.CONTACT)
	private String contact;

	@Masked(type = MaskingType.CREDIT_CARD)
	private String creditCard;
    
}

참조

0개의 댓글