Spring Security UsernamePasswordAuthenticationFilter: как получить доступ к запросу после неудачного входа в систему - PullRequest
0 голосов
/ 21 января 2019

Я реализую страницу входа в систему, используя Angular 7 и Spring Boot, и я получаю обработанную ошибку при входе в систему. По сути, я хочу на определенное время заблокировать вход в систему после неудачной попытки входа в систему.

Настройка HttpSecurity

@Override
    protected void configure(HttpSecurity http) throws Exception {
        logger.info("#### Configuring Security ###");
        JWTAuthenticationFilter jwtAuthenticationFilter = new JWTAuthenticationFilter(authenticationManager());
        jwtAuthenticationFilter.setFilterProcessesUrl("/rest/users/authenticate");//this override the default relative url for login: /login

        http
            .httpBasic().disable()
            .csrf().disable()
            .authorizeRequests()
            .antMatchers("/rest/", "/rest/helloworld/**").permitAll()
            .anyRequest().authenticated()
            .and().exceptionHandling().authenticationEntryPoint(new JwtAuthenticationEntryPoint()).and()
            .addFilter(jwtAuthenticationFilter);

Для обработки логина я создал фильтр

public class JWTAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
    private static Logger logger = Logger.getLogger(JWTAuthenticationFilter.class);

    private AuthenticationManager authenticationManager;

    public JWTAuthenticationFilter(AuthenticationManager authenticationManager) {
        this.authenticationManager = authenticationManager;

    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        try {
            UserDto credentials = new ObjectMapper().readValue((request.getInputStream()), UserDto.class);            
            return authenticationManager.authenticate(
                new UsernamePasswordAuthenticationToken(
                    credentials.getUserName(),
                    credentials.getPassword(),
                    new ArrayList<>())
            );
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
        //sucessfull authentication stuff
    }


    @Override
    protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {
        logger.info("Authentication failed");

        ErrorMessage errorMessage = new ErrorMessage("access_denied", "Wrong email or password.");
        String jsonObject = JSONUtil.toJson(errorMessage);

        //processing authentication failed attempt
        UserDto credentials = new ObjectMapper().readValue((request.getInputStream()), UserDto.class);
        AuthenticationService authenticationService = Application.getApplicationContext().getBean(AuthenticationService.class);
        int numFailedAttemptLogin = authenticationService.authenticationFailedAttempt(credentials.getUserName());

        response.setStatus(403);
        PrintWriter out = response.getWriter();
        out.print(jsonObject);
        out.flush();
        out.close();

        //super.unsuccessfulAuthentication(request, response, failed);
    }
}

Логин работает нормально, без проблем. Моя проблема связана с методом unsuccessfulAuthentication . Когда пользователь вводит неверные учетные данные, возникает исключение BadCredentials, и вызывается метод unsuccessfulAuthentication . Здесь мне нужно снова получить доступ к форме запроса, чтобы извлечь имя пользователя и обработать неудачная попытка аутентификации , и я получаю следующее исключение

java.io.IOException: Stream closed

Это потому, что внутри метода tryAuthentication входной поток запроса читается и, очевидно, закрывается.

Как я могу получить доступ к информации тела запроса в unsuccessfulAuthentication ?

Я пытался использовать SecurityContextHolder.getContext (). GetAuthentication (), но из-за сбоя аутентификации он пуст.

У кого-нибудь есть идеи?

С наилучшими пожеланиями

1 Ответ

0 голосов
/ 22 января 2019

После следующих M.Deinum предложений я смог создать компонент, который прослушивает определенные исключения:

@Component
public class AuthenticationEventListener implements ApplicationListener<ApplicationEvent> {
    private static Logger logger = Logger.getLogger(AuthenticationEventListener.class);

    @Override
    public void onApplicationEvent(ApplicationEvent applicationEvent) {
        logger.info(String.format("Event types: %s", applicationEvent.getClass()));
        if (applicationEvent instanceof AbstractAuthenticationFailureEvent) {
            String username = ((AbstractAuthenticationFailureEvent) applicationEvent).getAuthentication().getName();
            if (applicationEvent instanceof AuthenticationFailureBadCredentialsEvent) {
                logger.info(String.format("User %s failed to login", username));
                //this.handleFailureEvent(username, event.getTimestamp());
            }
        }

    }
}

Этот подход использует исключения для определения того, что делать в определенных сценариях. Я смог добиться чего-то подобного, продолжая использовать мой JWTAuthenticationFilter вот так

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        try {
            UserDto credentials = new ObjectMapper().readValue((request.getInputStream()), UserDto.class);
            try {
                return authenticationManager.authenticate(
                    new UsernamePasswordAuthenticationToken(
                        credentials.getUserName(),
                        credentials.getPassword(),
                        new ArrayList<>())
                );
            } catch (BadCredentialsException bce) {
                try {
                    handleBadCredentials(credentials, response);
                    throw bce;
                } catch (LockedException le) {
                    handleUserLocked(credentials, response);
                    throw le;
                }
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
        logger.info("Authentication failed");

        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
        response.setContentType(MediaType.TEXT_PLAIN_VALUE);
        response.getWriter().print(authException.getLocalizedMessage());
        response.getWriter().flush();
    }

Благодарю всех за потраченное время и помощь, высоко ценится.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...