2015-10-11 14 views
7

Używam Wiosna danych Boot REST utrzymują moje User podmiotyRunning @HandleBeforeCreate Po zatwierdzeniu podmiotu w Spring Boot Danych REST

@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 
} 

pomocą repozytorium

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

Co chcę zrobić sprawdza użytkowników: POST ed użytkowników:

@Configuration 
public class CustomRestConfiguration extends SpringBootRepositoryRestMvcConfiguration { 

    @Autowired 
    private Validator validator; 

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

} 

i tylko później hash hasła użytkownika przed zapisaniem go w DB:

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

    private PasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); 

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

Okazuje się jednak, walidacja odbywa się po mieszaja hasło i wskutek to nie ze względu na hashed hasło jest zbyt długa.

Czy istnieje sposób, aby nakazać Spring wykonać walidację najpierw i tylko wtedy hash hasłem? Wiem, że sam mógłbym napisać kontroler i określić wszystko w drobny sposób, ale wolałbym go zostawić w ostateczności.

+1

Jakiej wersji Spring Ramp/Data REST używasz? Właśnie przetestowałem to na mojej aplikacji i kiedy umieściłem punkt przerwania w obsłudze Repo i 'SizeValidatorForCharSequence', to w walidatorze trafia przed handler, więc dla mnie działa zgodnie z oczekiwaniami. Używam Spring Boot 1.2.5 –

+0

@BohuslavBurghardt Jestem na Spring Boot '1.2.6.RELEASE' i zarządza on wszystkimi innymi wersjami zależności. Naprawdę nie wiem, jak umieścić punkt przełomowy na walidatorze, ponieważ wypromowuję ten (jak sądzę) stworzony przez JPA. Zdarza się gdzieś za kulisami. Czy spróbuję to zrobić? – Wojtek

+0

@Wojtek, Czy istnieje jakikolwiek inny hak, za pomocą którego mogę uzyskać kontrolę po pomyślnym sprawdzeniu fasoli (przed utrwaleniem encji)? – masT

Odpowiedz

2

Jak zbadano debugera okazało się, że jednostka przychodzące przetwarzane w następującej kolejności:

  1. Wiosna wykonuje walidację fasoli po deserializacji JSON w SpringValidatorAdapter::validate. Hasło tutaj jest w postaci zwykłego tekstu.
  2. @HandleBeforeCreate jest wywoływane, a hasło jest hashowane.
  3. JPA dokonuje sprawdzenia podmiotu przed zapisaniem go w bazie danych. Hasło tutaj jest już hashed i sprawdzanie poprawności nie powiedzie się. W moim przypadku (implementacja WZP w Hibernate) walidację przeprowadzono w BeanValidationEventListener::validate.

Roztwór 1 (pełna walidacji w obu fazach)

Rozwiązaniem I stwierdzono rozluźnić ograniczenia w zakresie password używając tylko @NotEmpty (tak, że obie fazy walidacji przekazywane i nadal przychodzące JSON został sprawdzony pod kątem pustości/nieważności) i przeprowadzić walidację rozmiaru surowego hasła w @HandleBeforeCreate (i w razie potrzeby wyrzucić odpowiedni wyjątek).

Problem z tym rozwiązaniem polega na tym, że wymaga on napisania własnego programu obsługi wyjątku. Aby nadążyć za wysokimi standardami ustanowionymi przez Spring Data REST w odniesieniu do treści odpowiedzi na błąd, musiałbym napisać dużo kodu dla tego prostego przypadku. Sposób wykonania tego jest opisany here.

Roztwór 2 (Wiosna fasoli bez sprawdzania poprawności jednostki JPA)

jako zrozumienia przez Bohuslav Burghardt, można wyłączyć drugi etap sprawdzania wykonywane przez JPA. W ten sposób możesz zachować minimalne i maksymalne ograniczenia, a jednocześnie unikać pisania dodatkowego kodu. Jak zwykle jest to kompromis pomiędzy prostotą a bezpieczeństwem. Sposób wyłączenia WZP jest opisany here.

Rozwiązanie 3 (zachowując tylko min długość hasła przymusu)

Innym rozwiązaniem, przynajmniej w moim przypadku ważne było opuścić max długość hasła nieograniczona. W ten sposób w pierwszej fazie sprawdzania hasła sprawdzano, czy nie było zbyt krótko, a na drugim etapie było ono skutecznie sprawdzane za każdym razem (ponieważ zaszyfrowane hasło było już wystarczająco długie).

Jedynym zastrzeżeniem tego rozwiązania jest to, że @Size(min = 5) nie wydaje się sprawdzać pod kątem nieważności, więc musiałem dodać @NotNull, aby obsłużyć tę sprawę. W sumie pole jest opisane jako:

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

Interesujące czytanie. Powodem, dla którego to zadziałało jest prawdopodobnie to, że mam sprawdzanie hibernacji wyłączone przez application.properties ('spring.jpa.properties.javax.persistence.validation.mode = none'). Możesz to również zrobić, a następnie przeprowadzona zostanie tylko walidacja podczas deserializacji w kroku 1. –

+0

Tak, czytałem o tym, ale tak naprawdę nie chciałem zrezygnować z całego dobroci walidacji JPA :) Dodam to jako kolejne możliwe rozwiązanie. Dziękuję za pomoc Bohuslav! – Wojtek

+1

jak o sprawdzaniu poprawności String z Regex? – Kenji

Powiązane problemy