Spring Web оборачивает любые ошибки в 403 статусный код
Я пытаюсь написать микросервисный проект используя spring boot версии 3.4.5. Проблема возникла при добавлении в spring security необходимость быть авторизованным. Любая ошибка будет провоцировать возврат 403 кода. Даже встроенные в spring ошибки, по типу method not allowed или несуществующего endpoint вместо 405 и 404 соответственно вернут 403. Сейчас мой конфиг выглядит так:
@Configuration
@EnableWebSecurity
@EnableMethodSecurity
@RequiredArgsConstructor
public class SecurityConfig {
private final AuthFeign authFeign;
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf(AbstractHttpConfigurer::disable)
.cors(AbstractHttpConfigurer::disable)
.sessionManagement(smc -> smc.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authorizeHttpRequests(auth -> auth
.requestMatchers("/actuator/health", "/v3/api-docs").permitAll()
.anyRequest().authenticated()
)
.httpBasic(AbstractHttpConfigurer::disable)
.addFilterBefore(jwtAuthenticationFilter(workWithJwt()), UsernamePasswordAuthenticationFilter.class)
.addFilterBefore(apiKeyAuthFilter(), JwtAuthenticationFilter.class);
return http.build();
}
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration configuration) throws Exception {
return configuration.getAuthenticationManager();
}
@Bean
public JwtAuthenticationFilter jwtAuthenticationFilter(WorkWithJwt workWithJwt) {
return new JwtAuthenticationFilter(workWithJwt);
}
@Bean
public ApiKeyAuthFilter apiKeyAuthFilter() {
return new ApiKeyAuthFilter();
}
@Bean
@Profile("dev")
public WorkWithJwt workWithJwt() {
return new WorkWithJwt(authFeign);
}
}
Причем переключение с anyRequest().authenticated() на .permitAll() решает проблему. Собственно сам фильтр для jwt аутентификации:
public class JwtAuthenticationFilter extends OncePerRequestFilter {
private final WorkWithJwt jwtUtil;
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
String authHeader = request.getHeader("Authorization");
if (authHeader != null && authHeader.startsWith("Bearer ") && SecurityContextHolder.getContext().getAuthentication() == null) {
String token = authHeader.substring(7);
if (this.jwtUtil.validateToken(token)) {
this.jwtUtil.parseClaims(token);
UUID userId = this.jwtUtil.getUserIdFromToken(token);
String email = this.jwtUtil.getEmailFromToken(token);
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(email, (Object)null, List.of(new SimpleGrantedAuthority("ROLE_USER")));
authentication.setDetails(userId);
SecurityContextHolder.getContext().setAuthentication(authentication);
}
}
filterChain.doFilter(request, response);
}
@Generated
public JwtAuthenticationFilter(final WorkWithJwt jwtUtil) {
this.jwtUtil = jwtUtil;
}
}
Причем пробовал еще и реализацию через AuthenticationProvider, ничего не поменялось. Для проверок сделал так, чтобы один из методов сервиса кидал runtimeException. Даже так вернется не 500, а 403 код. Проверял наличие Authentification в контексте, какие данные там вообще лежат - все правильно: есть и principal, и authenticated true, все вообще правильно. Из authentication получаются данные, они валидны, используются в сервисах при обращении в бд - все работает. В ControllerAdvice нет перехвата всех ошибок и преобразование их в 403.
@RestControllerAdvice
public class GlobalExceptionHandler {
@ResponseBody
@ExceptionHandler(ConstraintViolationException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public ValidationErrorResponse onConstraintValidationException(
ConstraintViolationException ex
) {
final List<Violation> violations = ex.getConstraintViolations().stream()
.map(
violation -> {
Violation v = new Violation();
v.setMessage(violation.getMessage());
String[] s = violation.getPropertyPath().toString().split("\\.");
v.setFieldName(s[s.length - 1]);
return v;
}
).toList();
return new ValidationErrorResponse(violations);
}
@ExceptionHandler(ServiceException.class)
public ResponseEntity<String> exception(ServiceException e) {
return new ResponseEntity<>(e.getMessage(), e.getHttpStatus());
}
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<String> exception(MethodArgumentNotValidException e) {
return new ResponseEntity<>(e.getMessage(), HttpStatus.BAD_REQUEST);
}
@ExceptionHandler(MethodArgumentTypeMismatchException.class)
public ResponseEntity<String> exception(MethodArgumentTypeMismatchException e) {
return new ResponseEntity<>(e.getMessage(), HttpStatus.BAD_REQUEST);
}
}
Кто-то может подсказать решение проблемы? Буду рад любой помощи, если нужны доп материалы выложу.