Solucionado (ver solução)
Solucionado
(ver solução)
12
respostas

403 Forbidden - apenas na requisição de Consulta

Implementei os códigos para agendamento de consultas. O projeto rodou. Porém, no Insomnia, apenas a requisição "Agendar Consulta" é rejeitada (403 Forbiden), as outras funcionam normalmente.

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

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

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

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

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

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

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

12 respostas

Oi!

No seu controller tem um system.out imprimindo os dados da requisição. Esse texto apareceu no console?

Bom dia professor.

No console não apareceu o texto da requisição. Apenas os system.out do filtro, que eu mantive do curso anterior.

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

Ok. Manda aqui suas classes SecurityFilter e SecurityConfigurations

@Component
public class SecurityFilter extends OncePerRequestFilter{

    @Autowired
    private TokenService tokenService;

    //para injetar o repository neste filtro, para permitir uma consulta no BD
    @Autowired
    private UsuarioRepository repository;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws IOException, ServletException
    {
        //varíável que recebe o token a partir de um método que extrai o token
        var tokenJWT = recuperarToken(request);
        //System.out.println(tokenJWT);
        System.out.println("FILTRO SENDO CHAMADO!!!!");

        /*
         * Valida, se existir, o token e recupera o seu subject
         */
        if (tokenJWT != null){
            var subject = tokenService.getSubject(tokenJWT);
            var usuario = repository.findByLogin(subject);
           
            var authentication = new UsernamePasswordAuthenticationToken(usuario, null,usuario.getAuthorities());
            SecurityContextHolder.getContext().setAuthentication(authentication);

            System.out.println("LOGADO NA REQUISIÇÃO");
        }

        /*
         * para verificar se recuperou o subject do token
         */
        // System.out.println(subject);

        /*
         * para chamar o próximo filtro, encaminhando o request e o response
         */
        filterChain.doFilter(request, response);
    }

    private String recuperarToken(HttpServletRequest request) {
        var authorizationHeader = request.getHeader("Authorization");
        //se existe o cabeçalho
        if (authorizationHeader!=null){
            return authorizationHeader.replace("Bearer ", "");
        }
        return null;
    }
    
}

/*
 * como é uma classe de configurações, usamos a anotação @Configuration
 * dessa forma o Spring irá carregar essa classe no projeto
 */
@Configuration
/*
 * como vamos personalizar o processo de autenticação e de autorização
 * precisamos da anotação @EnableWebSecurity
 */
@EnableWebSecurity
public class SecurityConfiguration {

    //para injetar a classe SecurityFilter
    @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();
    }
}

@Configuration /*

  • como vamos personalizar o processo de autenticação e de autorização

  • precisamos da anotação @EnableWebSecurity */ @EnableWebSecurity public class SecurityConfiguration {

    //para injetar a classe SecurityFilter @Autowired private SecurityFilter securityFilter;

    /*

    • para que o spring leia o retorno desse método automaticamente
    • devemos inserir a anotação
    • @Bean
    • Essa anotação expõem o retorno deste método para o Spring */ @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(); }

}

Acho que deu problema no envio dessas classes.

Deve ser algum outro erro que está acontecendo, mas o Spring Security captura e devolve erro 403.

Altera a sua classe de tratamento de erros para tratar mais erros, conforme essa atividade: https://cursos.alura.com.br/course/spring-boot-aplique-boas-praticas-proteja-api-rest/task/125341

Dispare a requisição e veja no console e no retorno se mudou o erro.

Adicionei os outros métodos para tratamento de erros, mas não houve mudança no retorno.

@RestControllerAdvice public class TratadorDeErros{ //método para lidar com a Exception: EntityNotFoundException @ExceptionHandler(EntityNotFoundException.class) public ResponseEntity tratarErro404(){ return ResponseEntity.notFound().build(); //o build é para que ele crie o objeto ResponseEntity }

@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());
}

//DTO que retorna apenas os campos que desejamos, em caso de erro de validação
private record DadosErroValidacao(String campo, String mensagem){

    public DadosErroValidacao(FieldError erro){

        //chama o construtor padrão do DTO, passando o campo e a mensagem de erro
        this(erro.getField(), erro.getDefaultMessage());
    } 
}

}

Consegue compartilhar o teu projeto?

Professor, veja se consegue acessar:

https://github.com/benedditto/AgendamentoConsulta/tree/main/MedVoll

solução!

No seu projeto, no pacote controller. você criou uma anotação chamada @RestController. Apague esse arquivo e no ConsultaController importe essa anotação do Spring.

Opa, deu certo professor. Muito obrigado.