#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’, исключение не перехватывается, и я получаю трассировку стека в консоли и пустой ответ.