Solucionado (ver solução)

Importante

Você está vendo a versão anterior da nova experiência da Alura que estamos preparando para você. Em breve, ela ganha uma identidade visual novinha totalmente pensada em potencializar seus estudos!

Solucionado
(ver solução)
13
respostas

Erro nos outros endpoints apos autenticação

Eu queria na hora de fazer a autenticação que além do token e tipo de token me retornasse o nome do usuário e perfil(no meu caso se chama nível)

Na AutenticacaoController fiz o seguinte: (Que funciona na hora do login)

    Long nivelID = (long) 0;
    String nome = null;

    @PostMapping
    public ResponseEntity<TokenDto> autenticar(@RequestBody @Valid LoginForm form) {
        UsernamePasswordAuthenticationToken dadosLogin = form.convert();

        try {

            Authentication authentication = authManager.authenticate(dadosLogin);
            String token = tokenService.gerarToken(authentication);    

            buscaUsuario(dadosLogin.getPrincipal().toString());    

            return ResponseEntity.ok(new TokenDto(token, "Bearer", nome, nivelID ));

        } catch (AuthenticationException e) {
            System.out.println("Erro AuthenticationException - classe AutenticacaoController");
            return ResponseEntity.badRequest().build();
        }

    }

    @GetMapping
    public void buscaUsuario(String email) {

        List<LoginCustom2> result = lRepository.findByEmail(email);            
        for (LoginCustom2 l : result) {
            nome = l.getNome();
            nivelID = l.getNivel().getId();
        }        
    }

Mas começou a da erro nas outras requisições, porque toda requisição que faço passa por ali

Erro:

java.lang.NumberFormatException: For input string: "email@email"
    at java.base/java.lang.NumberFormatException.forInputString(NumberFormatException.java:67) ~[na:na]
    at java.base/java.lang.Long.parseLong(Long.java:711) ~[na:na]
    at java.base/java.lang.Long.parseLong(Long.java:836) ~[na:na]
    at br.com.example.config.security.TokenService.getIdUser(TokenService.java:50) ~[classes/:na]
    at br.com.example.config.security.AutenticacaoViaTokenFilter.autenticarUsuario(AutenticacaoViaTokenFilter.java:50) ~[classes/:na]
    at br.com.example.config.security.AutenticacaoViaTokenFilter.doFilterInternal(AutenticacaoViaTokenFilter.java:42) ~[classes/:na]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.3.12.jar:5.3.12]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336) ~[spring-security-web-5.5.3.jar:5.5.3]
    at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:103) ~[spring-security-web-5.5.3.jar:5.5.3]
    at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:89) ~[spring-security-web-5.5.3.jar:5.5.3]
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336) ~[spring-security-web-5.5.3.jar:5.5.3]
    at org.springframework.web.filter.CorsFilter.doFilterInternal(CorsFilter.java:91) ~[spring-web-5.3.12.jar:5.3.12]

Tem algum jeito melhor que posso fazer isso??

13 respostas

Oi Gabriela,

Parece que o problema é na hora de gerar o token. Posta aqui a sua classe TokenService

Não mudei nada nela, está como vi no curso

@Service
public class TokenService {

    @Value("${demo1.jwt.expiration}")
    private String expiration;

    @Value("${demo1.jwt.secret}")
    private String secret;

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

        return Jwts.builder()
                .setIssuer("API")
                .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 getIdUser(String token) {
        Claims claims = Jwts.parser().setSigningKey(this.secret).parseClaimsJws(token).getBody();
        return Long.parseLong(claims.getSubject());
    }

}

Antes de eu colocar pra controller me trazer nome e nível estava funcionando normal, creio que o erro seja na controller de autenticação mesmo

Entendi, mas a exception está sendo lançada na classe AutenticacaoViaTokenFilter. Posta ela aqui também.

public class AutenticacaoViaTokenFilter extends OncePerRequestFilter {

    private TokenService tokenService;
    private UsuarioRepository userRepository;

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

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

        String token = recuperarToken(request);

        // Verifica se o token gerado é valido
        boolean isTokenValid = tokenService.isTokenValido(token);

        if (isTokenValid) {
            autenticarUsuario(token);
        }

        filterChain.doFilter(request, response);

    }

    private void autenticarUsuario(String token) {
        Long idUser = tokenService.getIdUser(token);
        Login user = userRepository.findById(idUser).get();
        UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(user, null,
                user.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());
    }

}

Quando gera o token tem essa linha na classe TokenService:

.setSubject(logado.getId().toString())

Mas parece que no seu caso não está setando no subject o id do usuário, mas sim o email. E com isso dá erro nessa linha da classe AutenticacaoViaTokenFilter:

Long idUser = tokenService.getIdUser(token);

Porque não vem um Long e sim uma String.

Mas meu ID é Long mesmo

@Entity
@Table(name = "tbl_login")
public class Login implements UserDetails{

    private static final long serialVersionUID = 1L;

    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "login_id")
    private Long id;

    @Column(name = "login")
    private String email;
    private String senha;

Voce consegue compartilhar o seu projeto? Se tiver no github seria o ideal

Professor, desculpa a demora, mas de alguma forma funcionou, não sei o que fiz mas parou de dá esse erro rsrs

mas agora apareceu outro referente a isso. Na hora do PUT do login começou a aparecer um erro de Lazy, aparentemente minha entidade nível está com atraso e ai sempre que vou alterar um dado da esse erro, no banco a info é alterada sem problema mas acaba me retornando esse erro(500) apenas na primeira vez e na segunda vez me retorna 200

2021-12-09 08:15:31.999 ERROR 10512 --- [nio-8082-exec-6] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.http.converter.HttpMessageConversionException: Type definition error: [simple type, class org.hibernate.proxy.pojo.bytebuddy.ByteBuddyInterceptor]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No serializer found for class org.hibernate.proxy.pojo.bytebuddy.ByteBuddyInterceptor and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) (through reference chain: br.com.example.controller.dto.custom.LoginAddDto["nivel"]->br.com.example.model.custom.NivelAdd$HibernateProxy$DbWQLM2q["hibernateLazyInitializer"])] with root cause

com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No serializer found for class org.hibernate.proxy.pojo.bytebuddy.ByteBuddyInterceptor and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) (through reference chain: br.com.example.controller.dto.custom.LoginAddDto["nivel"]->br.com.example.model.custom.NivelAdd$HibernateProxy$DbWQLM2q["hibernateLazyInitializer"])
    at com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:77) ~[jackson-databind-2.12.5.jar:2.12.5]
    at com.fasterxml.jackson.databind.SerializerProvider.reportBadDefinition(SerializerProvider.java:1276) ~[jackson-databind-2.12.5.jar:2.12.5]
    at com.fasterxml.jackson.databind.DatabindContext.reportBadDefinition(DatabindContext.java:400) ~[jackson-databind-2.12.5.jar:2.12.5]
    at com.fasterxml.jackson.databind.ser.impl.UnknownSerializer.failForEmpty(UnknownSerializer.java:71) ~[jackson-databind-2.12.5.jar:2.12.5]
    at com.fasterxml.jackson.databind.ser.impl.UnknownSerializer.serialize(UnknownSerializer.java:33) ~[jackson-databind-2.12.5.jar:2.12.5]
    at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:728) ~[jackson-databind-2.12.5.jar:2.12.5]
    at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:770) ~[jackson-databind-2.12.5.jar:2.12.5]
    at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:178) ~[jackson-databind-2.12.5.jar:2.12.5]
    at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:728) ~[jackson-databind-2.12.5.jar:2.12.5]
    at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:770) ~[jackson-databind-2.12.5.jar:2.12.5]
    at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:178) ~[jackson-databind-2.12.5.jar:2.12.5]
    at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider._serialize(DefaultSerializerProvider.java:480) ~[jackson-databind-2.12.5.jar:2.12.5]
    at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.serializeValue(DefaultSerializerProvider.java:319) ~[jackson-databind-2.12.5.jar:2.12.5]
    at com.fasterxml.jackson.databind.ObjectWriter$Prefetch.serialize(ObjectWriter.java:1514) ~[jackson-databind-2.12.5.jar:2.12.5]
    at com.fasterxml.jackson.databind.ObjectWriter.writeValue(ObjectWriter.java:1006) ~[jackson-databind-2.12.5.jar:2.12.5]

Nivel

@Entity
@Table(name = "tbl_nivel_acesso")
public class NivelAdd {

    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "nivel_id")
    private Long id;

    @Column(name = "nome_nivel")
    private String nome;

    @Column(name = "data_criacao")
    private LocalDateTime dataCriacao = LocalDateTime.now();

    @Column(name = "data_ultima_alteracao")
    private LocalDateTime dataUltimaAlteracao = LocalDateTime.now();
}

Oi Gabriela,

Pode ser o jeito que foi implementando a funcionalidade PUT. Manda aqui o código do controller dessa funcionalidade.

    @PutMapping("/{id}")
    @Transactional
    public ResponseEntity<LoginAddDto> alteraLogin(@PathVariable Long id, 
            @RequestBody @Valid AlterLoginCustomForm form) {    
        System.out.println("\nid: "+id);
        Optional<LoginCustom> op = loginRepository.findById(id);
        if(op.isPresent()) {            
            LoginCustom s = form.atualizar(id, loginRepository,nRepository);            
            s.setSenha(config.criptografa(s.getSenha()));            
            return ResponseEntity.ok(new LoginAddDto(s));
        }
        return ResponseEntity.notFound().build();            
    }
solução!

Manda tambem suas classes AlterLoginCustomForm e LoginAddDto :D

Achei o erro prof, esqueci de setar a senha no atualizar() da classe AlterLoginCustomForm rsrs, desculpa o vacilo

Ah que bom que conseguiu resolver :D

Bons estudos!