Skip to content

Comparison

Before adopting Ihawu, it is worth knowing what you already have. Jackson ships two features that solve part of this problem, and your database probably ships a third. Ihawu’s Jackson backend is built on Jackson’s serializer SPI — it is not an alternative to Jackson (a kotlinx.serialization backend also ships) — and for a small enough problem it is not necessary at all.

Annotate properties with view marker classes and pick a view per request.

@JsonView(Views.Internal::class)
val socialSecurityNumber: String?

Use it when you have a handful of fixed response shapes, known at compile time, in a single service. It is free, it is built in, and for two or three shapes it is the right tool. Reaching for Ihawu here is overkill.

Where it runs out:

  • It can only include or exclude. There is no way to substitute a value, so a redacted ***-**-**** is not expressible — you can drop the SSN, but you cannot mask it.
  • Views are compile-time classes. Adding a role means writing a class and recompiling. With roles on one axis and sensitivity on another, view classes multiply.
  • The rules live in annotations on the type, so a policy change is a code change.

Annotate the type with a filter id and supply a FilterProvider at write time.

val filters = SimpleFilterProvider()
.addFilter("employee", SimpleBeanPropertyFilter.serializeAllExcept("socialSecurityNumber"))
mapper.writer(filters).writeValueAsString(employee)

Dynamic, and much closer to what Ihawu does. Read honestly, ihawu-core is a policy-driven, principal-aware @JsonFilter with the wiring and the policy model supplied for you.

Use it when one or two endpoints need runtime-varying exclusion. SimpleBeanPropertyFilter gets you there in about twenty lines.

Where it runs out:

  • Redaction means writing the engine yourself. SimpleBeanPropertyFilter includes and excludes; substituting a placeholder requires subclassing PropertyFilter and overriding serializeAsField. That is precisely the code Ihawu is.
  • Every call site has to be wired. In Spring that means wrapping each response in a MappingJacksonValue and attaching the provider. Miss one handler and it is unmasked.
  • There is no policy model. You write imperative code to decide the filter set for each caller; there is nothing to load from a database, a config file, or OPA.
  • Nesting is manual. A filter id has to be attached to each type you want filtered, and the filter set assembled for the whole graph.

Given a @JsonFilter can be bent into doing most of this, the value is in what you do not have to build:

@JsonView @JsonFilter Ihawu
Drop a field HIDE
Replace with a placeholder Custom PropertyFilter REDACT
Rules outside code (config, DB, OPA) ResourcePolicyResolver SPI
Add a role without recompiling
Wiring per call site Per handler Per handler ✅ None — register the module once
Nested types and collections Manual Manual ✅ Automatic
Behaviour when identity is missing Serializes Serializes ✅ Fails closed — emits {}

Two of those are worth drawing out.

Fail-closed on missing identity. If no IhawuPrincipal is attached to the write, Ihawu masks every field of the resource rather than serializing it. A forgotten wiring step produces an empty object, not a leak. With @JsonView or a hand-rolled filter, the failure mode of forgetting runs the other way.

No per-call-site wiring. IhawuModule is registered once on the ObjectMapper; the Spring Boot starter does even that for you. Controllers return their domain objects and there is nothing to remember at each handler — which matters, because the handler you forget is the one that leaks.

Column-level GRANTs, PostgreSQL row-level security, Oracle Data Redaction, and Snowflake dynamic data masking all mask at the source. Where they apply they are stronger than Ihawu: the sensitive value never enters your JVM at all, so it cannot reach a log, a cache, or an event stream. If your requirement is that an application must never hold a value, enforce it there, not here.

Ihawu is complementary, and earns its place where source-level masking cannot help: when the application legitimately needs the full value — to compute payroll, run a fraud check, populate an audit trail — but must not return it to this particular caller. The database cannot make that distinction, because from its side both reads look identical. Ihawu is the last-mile enforcement point for exactly that case.

For the sinks Ihawu does not cover, see How Ihawu Works.