2
respostas

[Dúvida] Tratamento de erro 403 não aparece na response

Olá estou tentando receber a mensagem 403 na response da chamada da API porem não aparece o texto, estou deixando algo faltar ?

eu fui seguindo os passos do curso "Spring Boot 3: aplique boas práticas e proteja uma API Rest"

Insira aqui a descrição dessa imagem para ajudar na acessibilidade

package med.voll.api.infra.exception;

import jakarta.persistence.EntityNotFoundException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import java.awt.*;
@RestControllerAdvice
public class TratadorDeErros {
@ExceptionHandler(EntityNotFoundException.class)
public ResponseEntity tratarErro404() {
return ResponseEntity.notFound().build();
}
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity tratarErro400(MethodArgumentNotValidException ex) {
var erros = ex.getFieldErrors();
return ResponseEntity.badRequest().body(erros.stream().map(DadosErroValidacao::new).toList());
}
@ExceptionHandler(HttpMessageNotReadableException.class)
public ResponseEntity tratarErro400(HttpMessageNotReadableException ex) {
return ResponseEntity.badRequest().body(ex.getMessage());
}
@ExceptionHandler(BadCredentialsException.class)
public ResponseEntity tratarErroBadCredentials() {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Credenciais inválidas");
}
@ExceptionHandler(AuthenticationException.class)
public ResponseEntity tratarErroAuthentication() {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Falha na autenticação");
}
@ExceptionHandler(AccessDeniedException.class)
public ResponseEntity tratarErroAcessoNegado() {
return ResponseEntity.status(HttpStatus.FORBIDDEN).body("Acesso negado");
}
@ExceptionHandler(Exception.class)
public ResponseEntity tratarErro500(Exception ex) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("Erro: " +ex.getLocalizedMessage());
}
private record DadosErroValidacao(String campo, String mensagem) {
public DadosErroValidacao(FieldError erro) {
this(erro.getField(), erro.getDefaultMessage());
}
}
}

package med.voll.api.infra.security;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
@Configuration
@EnableWebSecurity
public class SecurityConfigurations {
@Autowired
private SecurityFilter securityFilter;
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return
http.csrf(csrf -> csrf.disable())
.sessionManagement(sm -> sm.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authorizeHttpRequests(req -> {
req.requestMatchers("/login").permitAll();
req.anyRequest().authenticated();
})
.addFilterBefore(securityFilter, UsernamePasswordAuthenticationFilter.class)
.build();
}
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration configuration) throws Exception {
return configuration.getAuthenticationManager();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}

Garanta sua matrícula hoje e ganhe + 2 meses grátis

Continue sua jornada tech com ainda mais tempo para aprender e evoluir

Quero aproveitar agora
2 respostas

Ola! A imagem mostra uma requisição GET feita no Postman para http://localhost:8080/medicos, sem autenticação configurada (opção “No Auth”). O retorno da API é um status 403 Forbidden, sem nenhum corpo de resposta exibido.

Esse comportamento está correto com base no código que você enviou. A explicação é a seguinte:

  1. O Spring Security intercepta a requisição antes que ela chegue ao controlador, porque o endpoint /medicos exige autenticação (req.anyRequest().authenticated()).
  2. Como você não enviou token nem credenciais, o Spring bloqueia o acesso e retorna 403 Forbidden automaticamente.
  3. O seu TratadorDeErros não é acionado, porque o erro não é lançado dentro da camada de controle (Controller), e sim dentro do filtro de segurança do Spring.
  4. Por isso, o texto "Acesso negado" definido no método tratarErroAcessoNegado() não aparece — a resposta é gerada pelo próprio filtro do Spring Security, antes de chegar ao @RestControllerAdvice.

Para exibir uma mensagem personalizada no corpo da resposta 403, há duas formas possíveis:

Opção 1: Criar um AuthenticationEntryPoint personalizado e registrá-lo na configuração do HttpSecurity.
Exemplo:

import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.stereotype.Component;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;

@Component
public class CustomAccessDeniedHandler implements AccessDeniedHandler {
    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response,
                       org.springframework.security.access.AccessDeniedException accessDeniedException) throws IOException {
        response.setStatus(HttpServletResponse.SC_FORBIDDEN);
        response.setContentType("application/json");
        response.getWriter().write("{\"error\": \"Acesso negado\"}");
    }
}

E na sua classe SecurityConfigurations:

@Autowired
private CustomAccessDeniedHandler accessDeniedHandler;

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    return http
        .csrf(csrf -> csrf.disable())
        .sessionManagement(sm -> sm.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
        .authorizeHttpRequests(req -> {
            req.requestMatchers("/login").permitAll();
            req.anyRequest().authenticated();
        })
        .exceptionHandling(ex -> ex.accessDeniedHandler(accessDeniedHandler))
        .addFilterBefore(securityFilter, UsernamePasswordAuthenticationFilter.class)
        .build();
}

Com isso, o corpo da resposta 403 passará a conter o texto "Acesso negado".

Opa, vou tentar aqui depois obrigado pela ajuda