Запуск @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 ответов
как я исследовал в отладчике оказалось, что входящий объект обрабатывается в следующем порядке:
- Spring выполняет проверку бобов при десериализации JSON в
SpringValidatorAdapter::validate
. Пароль здесь в виде обычного текста. -
@HandleBeforeCreate
вызывается и пароль хэшируется. - 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;