Когда Spring Security не включен, любое исключение, созданное внутри моего контроллера, возвращается правильно как ответ об ошибке в Postman.
Однако, когда Spring Security включен, любое исключение (даже бизнес-логика или исключения времени выполнения из контроллеры) перехватывается, и я всегда получаю либо 401 Unauthorized, либо свой собственный ответ об ошибке аутентификации вместо фактического ответа об ошибке контроллера.
Похоже, что Spring Security обрабатывает исключения, не связанные с аутентификацией или авторизацией.
Я ожидаю:
- Ошибки аутентификации → обрабатываются AuthenticationEntryPoint
- Ошибки авторизации → обрабатываются AccessDeniedHandler
- Все остальные исключения → обрабатываются контроллером или глобальным обработчиком исключений
Ниже приведен мой код:
Реализация точки входа аутентификации:
public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request,
HttpServletResponse response,
AuthenticationException ex) throws IOException {
response.setStatus(HttpStatus.UNAUTHORIZED.value());
response.setContentType("application/json");
response.getWriter().write(
"{\"statusCode\":401,\"message\":\"" + ex.getMessage() + "\"}"
);
}
}
Реализация AccessDeniedHandler:
public class CustomAccessDeniedHandler implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest request,
HttpServletResponse response,
AccessDeniedException ex) throws IOException {
response.setStatus(HttpStatus.FORBIDDEN.value());
response.setContentType("application/json");
response.getWriter().write(
"{\"statusCode\":403,\"message\":\"" + ex.getMessage() + "\"}"
);
}
}
JwtFilterImplementation:
public class JwtAuthenticationFilter extends OncePerRequestFilter {
private final JwtService jwtService;
private final CustomUserDetailService userDetailService;
public JwtAuthenticationFilter(JwtService jwtService,
CustomUserDetailService userDetailService) {
this.jwtService = jwtService;
this.userDetailService = userDetailService;
}
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain chain) throws IOException, jakarta.servlet.ServletException {
String authHeader = request.getHeader("Authorization");
if (authHeader == null || !authHeader.startsWith("Bearer ")) {
chain.doFilter(request, response);
return;
}
String token = authHeader.substring(7);
if (!jwtService.tokenValidation(token)) {
chain.doFilter(request, response);
return;
}
String email = jwtService.extractAllClaims(token).getSubject();
var userDetails = userDetailService.loadUserByUsername(email);
var authentication = new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities()
);
SecurityContextHolder.getContext().setAuthentication(authentication);
chain.doFilter(request, response);
}
}
Конфигурация безопасности:
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig {
private final JwtAuthenticationFilter jwtAuthenticationFilter;
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http.authorizeHttpRequests(auth ->
auth
.requestMatchers("/public/**").permitAll()
.requestMatchers("/user/**").authenticated()
.requestMatchers("/admin/**").hasRole("ADMIN"))
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.formLogin(AbstractHttpConfigurer::disable)
.httpBasic(AbstractHttpConfigurer::disable)
.csrf(AbstractHttpConfigurer::disable)
.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
.exceptionHandling(ex -> {
ex.authenticationEntryPoint(new CustomAuthenticationEntryPoint());
ex.accessDeniedHandler(new CustomAccessDeniedHandler());
})
.build();
}
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception {
return config.getAuthenticationManager();
}
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
}
Подробнее здесь: https://stackoverflow.com/questions/798 ... 01-instead
Мобильная версия