Запуск @HandleBeforeCreate после проверки сущности в Spring Boot Data REST

Я использую Spring Boot Data REST для сохранения моего User объекты

@Entity
public class User {

    @Id
    @GeneratedValue
    private long id;

    @NotEmpty
    private String firstName;

    @NotEmpty
    private String lastName;

    @NotEmpty
    private String email;

    @Size(min = 5, max = 20)
    private String password;

    // getters and setters
}

использование репозитория:

public interface UserRepository extends CrudRepository<User, Long> {}

то, что я хочу сделать, это проверить POSTсначала пользователи ed:

@Configuration
public class CustomRestConfiguration extends SpringBootRepositoryRestMvcConfiguration {

    @Autowired
    private Validator validator;

    @Override
    protected void configureValidatingRepositoryEventListener(ValidatingRepositoryEventListener validatingListener) {
        validatingListener.addValidator("beforeCreate", validator);
    }

}

и только позже хэш пароль пользователя перед его хранением в БД:

@Component
@RepositoryEventHandler(User.class)
public class UserRepositoryEventHandler {

    private PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();

    @HandleBeforeCreate
    public void handleUserCreate(User user) {
         user.setPassword(passwordEncoder.encode(user.getPassword()));
    }
}

как оказалось, проверка выполняется после хэширования пароля, и в результате она терпит неудачу из-за hashed пароль слишком длинный.

есть ли способ поручить Spring сначала выполнить проверку, а затем только хэшировать пароль? Я знаю, что мог бы написать контроллер сам и уточнить все тонким способом, но я предпочел бы оставить это в качестве последнего средства.

1 ответов


как я исследовал в отладчике оказалось, что входящий объект обрабатывается в следующем порядке:

  1. Spring выполняет проверку бобов при десериализации JSON в SpringValidatorAdapter::validate. Пароль здесь в виде обычного текста.
  2. @HandleBeforeCreate вызывается и пароль хэшируется.
  3. JPA выполняет проверку сущности перед ее сохранением в БД. Пароль здесь уже хэшируется, и проверка не выполняется. В моем случае (реализация Hibernate JPA) проверка была выполнена в BeanValidationEventListener::validate.

Решение 1 (полная проверка на обоих этапах)

одним из решений, которое я нашел, было ослабить ограничение на password поле, просто используя @NotEmpty (так что обе фазы проверки прошли, и все еще входящий JSON был проверен на пустоту/ недействительность) и выполните проверку размера необработанного пароля в @HandleBeforeCreate (и бросьте соответствующее исключение оттуда, если это необходимо).

в проблема с этим решением заключается в том, что мне потребовалось написать собственный обработчик исключений. Чтобы идти в ногу с высокими стандартами, установленными Spring Data REST в отношении тела ответа на ошибку, мне пришлось бы написать много кода для этого простого случая. Способ сделать это описан здесь.

решение 2 (Проверка Spring bean без проверки сущности JPA)

а Богуслав Бургхардт можно отключить второй этап проверки, выполненный JPA. Таким образом, вы можете сохранить ограничения min и max и в то же время избежать написания дополнительного кода. Как всегда, это компромисс между простотой и безопасностью. Способ отключения JPA описан здесь.

решение 3 (сохранение только минимального ограничения длины пароля)

другим решением, по крайней мере, действительным в моем случае, было оставить максимальную длину пароля неограниченной. Этот путь в первом фаза проверки пароль был проверен, не был ли он слишком коротким, и на втором этапе он эффективно проверялся каждый раз (потому что зашифрованный пароль был уже достаточно длинным).

единственное предостережение к этому решению заключается в том, что @Size(min = 5) Не похоже, чтобы проверить на ничтожность, поэтому мне пришлось добавить @NotNull чтобы справиться с этим делом. Все во всем поле аннотируется как:

@NotNull
@Size(min = 5)
private String password;