Пользовательское неавторизованное сообщение о принятии решения при загрузке Spring Boot

#java #spring #spring-boot #spring-security

#java #spring #spring-boot #spring-безопасность

Вопрос:

У меня есть пользовательский AccessDecisionVoter для пользовательской аннотации, который ограничивает доступ к определенным методам на основе областей, указанных в аннотации. Это работает без проблем, но ответ, который я получаю, когда при проверке подлинности отсутствует область видимости, всегда один и тот же, и я ищу способ настроить это. Заранее благодарю вас.

ScopeVoter

 public class ScopeVoter implements AccessDecisionVoter<MethodInvocation> {

    @Override
    public int vote(Authentication authentication, MethodInvocation object, Collection<ConfigAttribute> attributes) {
        //custom logic here which decides to grant or deny the access. Here the message should be customized.
    }
  

Точка входа аутентификации

 @Component
public class AuthenticationEntryPointImp implements AuthenticationEntryPoint {

    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
        response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
    }
}
  

Именно здесь генерируется ответ, и я пытаюсь получить пользовательское сообщение для этого
Точка входа аутентификации.

Конфигурация HTTPSecurety

 @Override
protected void configure(HttpSecurity http) throws Exception {
    http.cors().and().csrf().disable()
            .exceptionHandling().authenticationEntryPoint(authenticationEntryPoint()).and()
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
            .authorizeRequests().antMatchers("/auth/**").permitAll()
            .anyRequest().authenticated();

    http.addFilterBefore(authFilter(), UsernamePasswordAuthenticationFilter.class);
}
  

AuthenticationEntryPoint() — это класс, описанный выше.

PS: Я также хочу получить пользовательское сообщение от AuthFilter () в точку входа.

РЕДАКТИРОВАТЬ: Мне было указано, что это может быть невозможно, но если не в AuthenticationEntryPoint, должно быть другое место. И я также хочу отобразить пользовательское сообщение из AuthenticationFilter в AuthenticationEntryPoint, которое определенно вызывается при сбое аутентификации.

Ответ №1:

если аутентификация использования прошла успешно, а затем избирателю отказано, вы можете настроить свой AccessDeniedHandler

 @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.cors().and().csrf().disable()
                .exceptionHandling().accessDeniedHandler(new AccessDeniedHandler() {
                @Override
                public void handle(HttpServletRequest request, HttpServletResponse response,
                        AccessDeniedException accessDeniedException) throws IOException, ServletException {
                    response.getWriter().println(accessDeniedException.getMessage());
                }
            }).authenticationEntryPoint(authenticationEntryPoint()).and()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
                .authorizeRequests().antMatchers("/auth/**").permitAll()
                .anyRequest().authenticated();
    
        http.addFilterBefore(authFilter(), UsernamePasswordAuthenticationFilter.class);
    }
  

затем в ScopeVoter вы можете самостоятельно вызвать исключение AccessDeniedException

 public class ScopeVoter implements AccessDecisionVoter<MethodInvocation> {

@Override
public int vote(Authentication authentication, MethodInvocation object, Collection<ConfigAttribute> attributes) {
    //if the logic is not met, throw it directly
    throw new AccessDeniedException("your customize message is here");
}
  

Комментарии:

1. Я не могу создать исключение в методе vote, поскольку в сигнатуре метода их нет.

2. AccessDeniedException — это исключение RuntimeException, поэтому сигнатура метода не требуется

3. Есть две реализации, AccessDeniedException в которых это не сработало… Отлично, это решило мою проблему с тем, как получить там пользовательское сообщение. Большое вам спасибо!

Ответ №2:

введите описание изображения здесь

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

Комментарии:

1. Хорошо, теперь я это вижу, но если нет, то в AuthenticationEntryPoint должно быть место, где я могу настроить «Запрещенное» сообщение.

2. смотрите мой ответ ниже

Ответ №3:

Что касается настраиваемой части ответа на ошибку в вашем вопросе: я настроил CustomAuthenticationEntryPoint так, чтобы он вызывал мой общий обработчик исключений, определенный с помощью @ControllerAdvice.

 @Component
public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint {

    @Autowired
    @Qualifier("handlerExceptionResolver")
    private HandlerExceptionResolver resolver;

    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException ex) throws IOException, ServletException {
        resolver.resolveException(request, response, null, ex);
    }
}
  

Исходный код доступен здесь.

Комментарии:

1. Я думаю, что это просто перенесет мою проблему в другой класс, поскольку я уже могу настроить сообщение в AuthenticationEntryPoint . Проблема заключается в получении данных, из-за которых произошел сбой аутентификации, из, AuthenticationFilter которого является OncePerRequestFilter , в AuthenticationEntryPoint ,

2. >>> Проблема заключается в получении данных, из-за которых произошел сбой аутентификации, из AuthenticationFilter. Причина должна быть в ex. Вы должны убедиться, что в OncePerRequestFilter только подклассах AuthenticationException выбрасывается, иначе ваш AuthenticationEntryPoint не будет вызван.

3. Я действительно думал об этом, но даже если я запускаю ‘AuthenticationException’, исключение не перехватывается, и я получаю трассировку стека в консоли и пустой ответ.