Spring Security, nie działa @ExceptionHandler

0

Mam standardową, prostą klasę do logowania w Springu. Sprawdzam przy logowaniu czy user ma wartość Active ustawioną na true, jeżeli nie to wywalam UserNotActivatedException. I w kontrolerze chciałbym przechwycić ten wyjątek, żeby wyświetlić własną stronę informującą, że konto nie jest aktywne. W rzeczywistości jednak, wyjątek jest generowany, ale metoda z @ExceptionHandler nie jest wywoływana i jedyne co się dzieje, to strona do logowania się odświeża. Dlaczego wyjątek nie jest przechwytywany w kontrolerze ?

@Service
public class UserServiceImpl implements UserService {

    @Override
    public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
       User user = userRepository.findByEmail(email).get();
        return createUser(user);
    }

    private org.springframework.security.core.userdetails.User createUser(User user) {
        if(!user.isActive()) {
            throw new UserNotActivatedException(":<");
        }
//dalszy kod
    }
}

@Controller
@Slf4j
public class UserController {

    @ExceptionHandler(UserNotActivatedException.class)
    public ModelAndView handleNotFoundException(Exception exception) {
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.setViewName("strona_z_bledem");
        return modelAndView;
    }

}

0

Ten UserNotActivatedException to Twoj wyjatek? Jaka ma definicje?
Z dokumentacji: An exception argument: declared as a general Exception or as a more specific exception.

0

Tak, mój.

public class UserNotActivatedException extends AuthenticationException {

    public UserNotActivatedException(String message) {
        super(message);
    }
}
0

Adnotacja @ExceptionHandler sama w sobie łapie wyjątki rzucone tylko w zakresie requesta bieżącego controllera. Jeśli chcesz handlować wyjątkami globalnie, dorzuć adnotację @ControllerAdvice nad kontrolerem. Po więcej zapraszam tutaj:
https://spring.io/blog/2013/11/01/exception-handling-in-spring-mvc (2013 ale aktualne)

0

spróbuj @ControllerAdvice zamiast @Controller

0

Próbowałem z @ControllerAdvice też. Stworzyłem taką nową klasę w tym samym pakiecie co UserController, ale bez skutku.

@ControllerAdvice
public class ExceptionHandlerController {

    @ExceptionHandler(UserNotActivatedException.class)
    public ModelAndView handleNotFoundException(Exception exception) {
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.setViewName("index");
        modelAndView.setStatus(HttpStatus.NOT_FOUND);
        return modelAndView;
    }

}

0

Okej, to dwie rzeczy do sprawdzenia:

  1. Na pewno ci ten pakiet z controllerami Spring skanuje?
  2. Postaw breakpointa wewnatrz tej metody w której chcesz obsłużyć błąd i zdebuguj. Bo może exception przechwytujesz, ale błąd leży w implementacji metody przechwytującej.
0

a spróbuj zmienić typ argumentu metody na UserNotActivatedException

Zmieniłem, niestety nie pomogło.

  1. Na pewno ci ten pakiet z controllerami Spring skanuje?

Tak. Wykrywa mi w tym pakiecie wszystkie inne adnotacje @Controller, @GetMapping itd. Poza tym w jednym z kontrolerów z tego pakietu mam już jedną metodę z @ExceptionHandler, która łapie mi wyrzucany wyjątek z warstwy serwisowej i ona działa prawidłowo.

  1. Postaw breakpointa wewnatrz tej metody w której chcesz obsłużyć błąd i zdebuguj. Bo może exception przechwytujesz, ale błąd leży w implementacji metody przechwytującej.

To już też robiłem. Ustawiłem jednego breakpointa w throw new UserNotActivatedException(":<"); i drugiego w tej właśnie metodzie z @ExceptionHandler w linijce powiedzmy modelAndView.setViewName("exceptions/notFound");. Na pierwszym breakpoincie debugger mi się zatrzymuje, na drugim już nie. Wniosek taki, że ten wyjątek w ogóle nie jest przechwytywany.

Do wniosków doszedłem takich (chociaż nie muszę mieć racji), że między wyrzuceniem Exceptiona, a złapaniem go w @ExceptionHandler, Spring wykonuje jakieś swoje operacje i to na nich zatrzymuje się sterowanie programu. Dokładnie mam na myśli: https://docs.spring.io/spring-security/site/docs/4.2.4.RELEASE/apidocs/org/springframework/security/web/authentication/AuthenticationFailureHandler.html, któraś domyślna, implementująca ten interfejs klasa Springa, może być skonfigurowana w ten sposób, że po login failed ma automatycznie wyświetlić użytkownikowi jeszcze raz formularz logowania i mój wyjątek w takiej sytuacji nie zostaje przechwycony. Pytanie jak to teraz zmienić.

0

z tego co kojarze to controller advice dzizla jak jest juz niefachowo mowiac kontekst requestu oblusigwany. Co np skutkutje tym ze jezeli z filtrow secuirity bedziesz rzucac runtime, to excepetion handler tego nie zlapie.

0
filemonczyk napisał(a):

z tego co kojarze to controller advice dzizla jak jest juz niefachowo mowiac kontekst requestu oblusigwany. Co np skutkutje tym ze jezeli z filtrow secuirity bedziesz rzucac runtime, to excepetion handler tego nie zlapie.

No i właśnie nie łapie. Stworzyłem sobie już public class LoginFailedHandler implements AuthenticationFailureHandler i mam w tym miejscu pełną kontrole nad tym co będzie się działo po login failu (testowałem). Jak w tym miejscu teraz sprawdzić czy został rzucony UserNotActivatedException ?

przestań dziedziczyć po AuthenticationException

Czemu ?

0

Bo przez to Spring łapie twój wyjątek.

0

Wcześniej ten wyjątek rozszerzał RuntimeException i było to samo.

0

Jesteś w stanie odtworzyć ten błąd w jakimś projekcie, który możesz wrzucić na github?

Jest to do zrobienia. Napiszę tutaj posta jak znajdę chwilę czasu żeby to wydzielić.

0

a btw to import org.springframework.security.authentication.DisabledException;
pozniej mozesz handlery nadpisac nad to , ja tak chyba gdzies robilem
https://docs.spring.io/spring-security/site/docs/4.2.4.RELEASE/apidocs/org/springframework/security/authentication/DisabledException.html

0

Generalnie to już powiedzmy w 90% osiągnąłem to co chciałem właśnie przez zaimplementowanie własnego handlera. Sprawdzam sobie getClass() wyrzucanego wyjątku i w zależności od tego podejmuję działania. Problemem nadal tylko jest, że w konsoli drukuje mi stacktrace tego wyjątku i nie wygląda to profesjonalnie. Pytanie jak się tego prosto pozbyć.

0

Sprawdź kolejność filtrów w spring security, może być tak że masz coś poknocone i wyjątek leci ci z filtra i jest łapany dopiero przez kontener servletów (np catalinę). Kojarzę jakiś problem związany z tym i customowym PreAuthenticationFilter. Wyjątki z łańcucha filtrów spring security są łapane przez ExceptionTranslationFilter który deleguje akcję w przypadku błędu autentykacji do AuthenticationEntryPoint. Nie grzebałem przy tym dużo (w zasadzie wcale) ale warto sobie odpalić debuger i ogarnąć jak te filtry są ułożone.

0

@bames

https://github.com/Janusz3000/security-test

Tak więc jeszcze raz: rzucam wyjątek przy logowaniu w warstwie serwisowej i problem polega na tym, że nie jest łapany w kontrolerze przez @ExceptionHandler.

0

Sprawdź kolejność filtrów w spring security, może być tak że masz coś poknocone i wyjątek leci ci z filtra i jest łapany dopiero przez kontener servletów (np catalinę). Kojarzę jakiś problem związany z tym i customowym PreAuthenticationFilter. Wyjątki z łańcucha filtrów spring security są łapane przez ExceptionTranslationFilter który deleguje akcję w przypadku błędu autentykacji do AuthenticationEntryPoint. Nie grzebałem przy tym dużo (w zasadzie wcale) ale warto sobie odpalić debuger i ogarnąć jak te filtry są ułożone.

Sprawdziłem, nie na tym problem polega, wszystkie filtry mam ustawione domyślnie.

1

Problem wynika z tego, że Twój user detail service jest providerem dla DaoAuthenticationProvider.
Wystarczy spojrzeć na stack:

org.springframework.security.authentication.InternalAuthenticationServiceException: :<
	at org.springframework.security.authentication.dao.DaoAuthenticationProvider.retrieveUser(DaoAuthenticationProvider.java:119) ~[spring-security-core-5.0.5.RELEASE.jar:5.0.5.RELEASE]
	at org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider.authenticate(AbstractUserDetailsAuthenticationProvider.java:144) ~[spring-security-core-5.0.5.RELEASE.jar:5.0.5.RELEASE]
	at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:174) ~[spring-security-core-5.0.5.RELEASE.jar:5.0.5.RELEASE]
	at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:199) ~[spring-security-core-5.0.5.RELEASE.jar:5.0.5.RELEASE]
	at org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter.attemptAuthentication(UsernamePasswordAuthenticationFilter.java:94) ~[spring-security-web-5.0.5.RELEASE.jar:5.0.5.RELEASE]
	at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:212) ~[spring-security-web-5.0.5.RELEASE.jar:5.0.5.RELEASE]

bla bla bla

Caused by: com.example.demo.exception.UserNotActivatedException: :<
	at com.example.demo.service.UserService.loadUserByUsername(UserService.java:33) ~[classes/:na]
	at org.springframework.security.authentication.dao.DaoAuthenticationProvider.retrieveUser(DaoAuthenticationProvider.java:104) ~[spring-security-core-5.0.5.RELEASE.jar:5.0.5.RELEASE]
	... 54 common frames omitted

Wyjątek który rzucasz jest obsłużony przez spring security. Chyba najlepiej zaimplementować własny authetnication provider i tam już obsługiwać konkretne wyjątki, ale nie czuje się pewnie w spring security więc warto poszukać innych rozwiązań.

1 użytkowników online, w tym zalogowanych: 0, gości: 1