Files
spring-framework/framework-docs/modules/ROOT/partials/web/web-data-binding-model-design.adoc
T
2026-09-02 14:45:26 +02:00

69 lines
3.3 KiB
Plaintext

Data binding involves binding untrusted input onto application objects.
For security reasons, it's crucial to ensure that input is properly constrained to expected fields only.
This section provides guidance for safe binding.
First, prefer **immutable object design** for web binding purposes.
It is safe because a constructor naturally constrains binding to expected inputs.
You can use a Java record or a class with a primary constructor, and either can have further nested objects.
See xref:core/validation/data-binding.adoc#data-binding-constructor-binding[Constructor Binding] for details.
Another option for safe binding is to use **dedicated objects** designed for the expected input.
Such objects, even if mutable, are safe because they constrain binding to the expected inputs.
Domain objects such as JPA or Hibernate entities are generally not safe for web binding
as they likely contain more properties than the expected inputs.
For such cases, it's crucial to declare the properties to expose for binding.
For example:
[source,java,indent=0,subs="verbatim,quotes"]
----
@Controller
public class PersonController {
@InitBinder
void initBinder(WebDataBinder binder) {
// See Javadoc for supported pattern syntax
binder.setAllowedFields("firstName", "lastName", "*Address");
}
}
----
NOTE: The `disallowedFields` property has been
https://github.com/spring-projects/spring-framework/issues/36802[deprecated in Spring Framework 7.1]
because it is fragile and easy to get out of sync with the actual properties over time.
The patterns given to `allowedFields` and `disallowedFields` are not limited to top-level
field names. They are property paths, using the same syntax supported for reading and
writing bean properties elsewhere in the Framework. They also support `*` as a
wildcard; this means you can constrain binding more precisely:
* `"address"` matches the `address` property.
* `"person.address.city"` matches the `city` property of the nested `address` property of `person`.
* `"addresses[0].city"` matches the `city` property of the element at index `0` in the `addresses` array or `List`.
* `"map[key]"` matches the entry associated with `key` in the `map` property.
* `"map*"` matches every entry in the `map` property, such as `"map[key1]"` and `"map[key2]"`.
the same wildcard syntax also applies to indexed elements in an array or `List`.
See the {spring-framework-api}/validation/DataBinder.html#setAllowedFields(java.lang.String...)[`DataBinder#setAllowedFields`]
javadoc for further details on the supported pattern syntax.
By default, `DataBinder` applies both constructor and setter binding.
This is fine with immutable objects and dedicated objects, but for domain objects, you must
remember to set `allowedFields`. To ensure data binding is only used in declarative style where
expected inputs are explicitly declared, you can set `declarativeBinding` on `DataBinder`.
That applies constructor binding always, and setter binding conditionally if `allowedFields` is set.
The following shows how to set this flag globally, or
you can also narrow it through attributes on `ControllerAdvice`:
[source,java,indent=0,subs="verbatim,quotes"]
----
@ControllerAdvice
public class ControllerConfig {
@InitBinder
void initBinder(WebDataBinder binder) {
binder.setDeclarativeBinding(true);
}
}
----