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

Erro ao testar delete

package br.com.alura.forum.config.swagger;

import java.util.Arrays;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import br.com.alura.forum.modelo.Usuario;
import springfox.documentation.builders.ParameterBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.schema.ModelRef;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;

@Configuration
public class SwaggerConfigurations {

    @Bean
    public Docket forumApi() {
        return new Docket(DocumentationType.SWAGGER_2)
                .select()
                .apis(RequestHandlerSelectors.basePackage("br.com.alura.forum"))
                .paths(PathSelectors.ant("/**"))
                .build()
                .ignoredParameterTypes(Usuario.class)
                .globalOperationParameters(
                        Arrays.asList(
                                new ParameterBuilder()
                                    .name("Authorization")
                                    .description("Header para Token JWT")
                                    .modelRef(new ModelRef("string"))
                                    .parameterType("header")
                                    .required(false)
                                    .build()));

    }

}

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

15 respostas
package br.com.alura.forum.config.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.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import br.com.alura.forum.repository.UsuarioRepository;

@EnableWebSecurity
@Configuration
public class SecurityConfigurations extends WebSecurityConfigurerAdapter{

    @Override
    @Bean // para trabalhar com autenticacao jwt tem que sobrescrever esse metodo aqui para poder injetar o AuthenticationManager em outra classe
    protected AuthenticationManager authenticationManager() throws Exception {

        return super.authenticationManager();
    }

    @Autowired
    private UsuarioRepository usuarioRepository; 

    @Autowired
    private TokenService token;

    @Autowired
    private AutenticacaoService autenticacaoService;

    @Override    // configuracoes de autenticacao
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(autenticacaoService).passwordEncoder(new BCryptPasswordEncoder()); //vai informar onde ta a classe com a logica de autenticacao

    }
    @Override  // configuracoes de autorizacao
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
            .antMatchers(HttpMethod.GET, "/topicos").permitAll() //vai permitir o get do endereco /topicos
            .antMatchers(HttpMethod.GET, "/topicos/*").permitAll()
            .antMatchers(HttpMethod.POST, "/auth").permitAll()
            .antMatchers(HttpMethod.GET, "/actuator/**").permitAll()
            .anyRequest().authenticated() // qualquer outra requisicao tem que autenticar; 
            .and().csrf().disable()
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            .and().addFilterBefore(new AutenticacaoViaTokenFilter(token, usuarioRepository), UsernamePasswordAuthenticationFilter.class);  
    }
    @Override // configuracoes de recursos estaticos(javascript, css, imagens, etc)
    public void configure(WebSecurity web) throws Exception {
         web.ignoring()
            .antMatchers("/**.html", "/v2/api-docs", "/webjars/**", "/configuration/**", "/swagger-resources/**");
    }


}

Oi Artur,

Posta a exception completa aqui. Obs: verificou se existe o topico de id 6 no banco antes de fazer o delete?

Olá, verifiquei sim e tá la no get listar, o a excecption é muito grande dai peguei o começo e o final: Insira aqui a descrição dessa imagem para ajudar na acessibilidade Insira aqui a descrição dessa imagem para ajudar na acessibilidade

"message": "Cannot invoke "Object.hashCode()" because "value" is null", "path": "/topicos/7" }

Alguma novidade ?

Oi Artur,

Desculpe a demora. Parece que o erro está estourando na sua classe AutenticacaoViaTokenFilter.

Posta o código dela aqui e também o código do seu controller e do TokenService.

Sem problema !

package br.com.alura.forum.config.security;

import java.io.IOException;
import java.util.Optional;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.filter.OncePerRequestFilter;

import br.com.alura.forum.modelo.Usuario;
import br.com.alura.forum.repository.UsuarioRepository;


public class AutenticacaoViaTokenFilter extends OncePerRequestFilter {


    private UsuarioRepository repository;

    private TokenService tokenService;

    public AutenticacaoViaTokenFilter(TokenService tokenService, UsuarioRepository repository) {
        this.tokenService = tokenService;
        this.repository = repository;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {

        String token = recuperarToken(request);

        boolean valido = tokenService.isTokenValido(token);
        if(valido) {
            autenticarCliente(token);
        }

        filterChain.doFilter(request, response);
    }

    private void autenticarCliente(String token) {
        Long idUsuario = tokenService.getIdUsuario(token);
        Usuario usuario = repository.findById(idUsuario).get();
        UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(usuario, null, usuario.getAuthorities());
        SecurityContextHolder.getContext().setAuthentication(authentication);

    }

    private String recuperarToken(HttpServletRequest request) {
        String token = request.getHeader("Authorization");
        if(token == null || token.isEmpty() || !token.startsWith("Bearer ")) {
        return null;
    }
        return token.substring(7, token.length());

    }
}
@EnableWebSecurity
@Configuration
public class SecurityConfigurations extends WebSecurityConfigurerAdapter{

    @Override
    @Bean // para trabalhar com autenticacao jwt tem que sobrescrever esse metodo aqui para poder injetar o AuthenticationManager em outra classe
    protected AuthenticationManager authenticationManager() throws Exception {

        return super.authenticationManager();
    }

    @Autowired
    private UsuarioRepository usuarioRepository; 

    @Autowired
    private TokenService token;

    @Autowired
    private AutenticacaoService autenticacaoService;

    @Override    // configuracoes de autenticacao
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(autenticacaoService).passwordEncoder(new BCryptPasswordEncoder()); //vai informar onde ta a classe com a logica de autenticacao

    }
    @Override  // configuracoes de autorizacao
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
            .antMatchers(HttpMethod.GET, "/topicos").permitAll() //vai permitir o get do endereco /topicos
            .antMatchers(HttpMethod.GET, "/topicos/*").permitAll()
            .antMatchers(HttpMethod.POST, "/auth").permitAll()
            .anyRequest().authenticated() // qualquer outra requisicao tem que autenticar; 
            .and().csrf().disable()
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            .and().addFilterBefore(new AutenticacaoViaTokenFilter(token, usuarioRepository), UsernamePasswordAuthenticationFilter.class);  
    }
    @Override // configuracoes de recursos estaticos(javascript, css, imagens, etc)
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/**.html", "/v2/api-docs", "/webjars/**","/configuration/**", "/swagger-resources/**");
    }


}

Posta também o TokenService e TopicosController

package br.com.alura.forum.config.security;

import java.util.Date;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Service;

import br.com.alura.forum.modelo.Usuario;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

@Service
public class TokenService {

    @Value("${forum.jwt.expiration}") // injetando uma propriedade do application.properties
    private String expiration;

    @Value("${forum.jwt.secret}") // injetando uma propriedade do application.properties
    private String secret;

    public String gerarToken(Authentication authentication) {
        Usuario logado = (Usuario) authentication.getPrincipal();
        Date hoje = new Date();
        Date dataExpiracao = new Date(hoje.getTime() + Long.parseLong(expiration));

        return Jwts.builder()
                .setIssuer("API do Fórum da Alura")
                .setSubject(logado.getId().toString())
                .setIssuedAt(hoje)
                .setExpiration(dataExpiracao)
                .signWith(SignatureAlgorithm.HS256, secret)
                .compact();
    }

    public boolean isTokenValido(String token) {

        try {
            Jwts.parser().setSigningKey(this.secret).parseClaimsJws(token);
            return true;
        } catch (Exception e) {
            return false;
        }


    }

    public Long getIdUsuario(String token) {
        Claims claims = Jwts.parser().setSigningKey(this.secret).parseClaimsJws(token).getBody();
        return Long.parseLong(claims.getSubject());
    }

}
package br.com.alura.forum.controller;

import java.net.URI;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;

import javax.transaction.Transactional;
import javax.validation.Valid;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.web.PageableDefault;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.util.UriComponentsBuilder;


@RestController
@RequestMapping("/topicos")
public class TopicosController {

    @Autowired
    private TopicoRepository topicoRepository;

    @Autowired
    private CursoRepository cursoRepository;

    @GetMapping        
    @Cacheable(value = "listaDeTopicos") //esse value vai identificar o cache desse metodo         // se nao receber nenhum parametro na url, vai ordenar pelo id decrescente, a partir da pagina 0, 10 por pagina
    public Page<TopicoDto> lista(@RequestParam(required = false) String nomeCurso, @PageableDefault(sort = "id", direction = Direction.DESC, page = 0, size = 10) Pageable paginacao) { //requestparam avisa pro spring que esses parametros sao de url, obrigatorio // required=false diz que o nome do curso eh opcional


        if(nomeCurso == null) {
            Page<Topico> topicos = topicoRepository.findAll(paginacao);

            return TopicoDto.converter(topicos);
        }
        else {    
            Page<Topico> topicos = topicoRepository.findByCursoNome(nomeCurso, paginacao); // utilizando findBy... é só criar o método no repository que o springdata gera a query automaticamente de acordo com o que vem depois do By

            return TopicoDto.converter(topicos);

        }
    }
    @PostMapping
    @Transactional
    @CacheEvict(value = "listaDeTopicos", allEntries = true)  // vai limpar o cache pra poder ser atualizado
    public ResponseEntity<TopicoDto> cadastrar(@RequestBody @Valid TopicoForm form, UriComponentsBuilder uriBuilder) { // quando é Get, o parametro vem na url, quando é Post, tem que avisar ao spring que o parametro vem no corpo
                                                                                                                        // o @valid  vai validar o que vem do topicoform, que a gente anotou com os requisitos
        Topico topico = form.converter(cursoRepository);
        topicoRepository.save(topico);


        URI uri = uriBuilder.path("/topicos/{id}").buildAndExpand(topico.getId()).toUri();
        return ResponseEntity.created(uri).body(new TopicoDto(topico));
    }
    @GetMapping("/{id}")  // parametro dinamico
    public ResponseEntity<DetalhesDoTopicoDto> detalhar(@PathVariable Long id) {   // esse parametro nao vai vir na interrogacao, ele vai vir na /

        Optional<Topico> topico = topicoRepository.findById(id);
        if(topico.isPresent()) {
            return ResponseEntity.ok(new DetalhesDoTopicoDto(topico.get()));
        }
        return ResponseEntity.notFound().build();
    }
    @PutMapping("/{id}")
    @Transactional
    @CacheEvict(value = "listaDeTopicos", allEntries = true)
    public ResponseEntity<TopicoDto> atualizar(@PathVariable Long id, @RequestBody @Valid AtualizacaoTopicoForm form){
        Optional<Topico> optional = topicoRepository.findById(id);
        if(optional.isPresent()) {
            Topico topico = form.atualizar(id, topicoRepository);
            return ResponseEntity.ok(new TopicoDto(topico));
        }
        return ResponseEntity.notFound().build();



    }
    @DeleteMapping("/{id}")
    @Transactional
    @CacheEvict(value = "listaDeTopicos", allEntries = true)
    public ResponseEntity<TopicoDto> remover(@PathVariable Long id) {
        Optional<Topico> optional = topicoRepository.findById(id);
        if(optional.isPresent()) {
            topicoRepository.deleteById(id);

            return ResponseEntity.ok().build();
        }
            return ResponseEntity.notFound().build();


    }
}

Não consegui identificar o problema :(

Consegue compartilhar o seu projeto? Pode ser via GitHub ou link do google drive

Coloquei agora no githug, é o https://github.com/Shadwz1NN/Forum_API Utilizei o mysql

Oi Artur,

Eu testei aqui o seu projeto e pra mim funcionou. Consegui realizar o delete normalmente:

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

Talvez algum problema com a sua versão do Java. Estou utilizando o Java 17 no meu computador.

solução!

Tambem uso o java 17, já tentei tudo que consegui mas não deu, vou pular essa parte. Obrigado !

Quer mergulhar em tecnologia e aprendizagem?

Receba a newsletter que o nosso CEO escreve pessoalmente, com insights do mercado de trabalho, ciência e desenvolvimento de software