11
respostas

Token inválido

Toda vez que a aplicação reinicia, o token é inválidado, tem alguma forma que eu possa alterar isso?

11 respostas

Oi Victor,

Provável que seja algum problema de configuração no seu projeto, pois no restart do servidor o token deveria continuar válido. Somente expira após o tempo configurado na criação do token. Verifica sua classe que cria o token se o tempo de expiração está sendo passado corretamente.

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

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

A princípio tudo ok.

Mas precisaria ver como você está executando a aplicação e disparando a requisição para obter o token.

No projeto do curso, ao fazer login você recebe um token e pode reiniciar a aplicação sem problemas, pois o mesmo token continua válido para as próximas requisções.

Acho que isso começou quando eu coloquei o @AuthenticationPrincipal

Testei aqui colocando isso no método de cadastrar tópico e funcionou normal também:

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

estou usando UUID, será que é por isso? toda vez que a aplicação reinicia, o ID do usuário troca.

Pior que não, pois o jjwt não checa as informações do token, apenas se ele é válido.

Posta aqui suas classes TokenService, AutenticacaoViaTokenFilter e SecurityConfigurations

package application.service.serviceAction;


import application.dto.response.UserMonitoringDTO;
import application.entity.User;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Service;

import java.util.Date;

@Service // Indica que é uma camada de serviço , o spring vai gerenciar automaticamente.
public class TokenServiceImpl implements TokenService { // Serviço relacionado ao token.

    @Value("${jwt.expiration}") // var de ambiente , localização externa - application(test,prod).properties
    private String expiration; // tempo de expiração do token

    @Value("${jwt.secret}") // var de ambiente , localização externa - application(test,prod).properties
    private String secret; // regra de como o token será codificado.

    @Override
    public String generateToken(Authentication authentication) { // Método que gera um token.
        User logged = (User) authentication.getPrincipal();

        Date today = new Date();

        Date dateExpiration = new Date(today.getTime() + Long.parseLong(expiration));

        UserMonitoringDTO userDTO = new UserMonitoringDTO(logged);

        return Jwts.builder()                          // -> método cria o hash (token) inteiro com dados do usuario, segurança e alghoritmo de codificação e compacta.
                .setSubject(userDTO.getId())
                .claim("username", userDTO.getNickname())
                .claim("email", userDTO.getEmail())
                .claim("roles", userDTO.getRolesDTO())
                .setIssuedAt(today)
                .setExpiration(dateExpiration)
                .signWith(SignatureAlgorithm.HS256, secret)
                .compact();
    }

    @Override
    public boolean isTokenValid(String token) { // Método que verifica se o Token é válido.
        try {
            Jwts.parser().setSigningKey(this.secret).parseClaimsJws(token);
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    @Override
    public String getIdUser(String token) { // Método que recupera o Id do Usuário pelo token.
        Claims claims = Jwts.parser().setSigningKey(this.secret).parseClaimsJws(token).getBody();
        return claims.getSubject();
    }
}
package application.filter;


import application.entity.User;
import application.repository.UserRepository;
import application.service.exception.DatabaseException;
import application.service.serviceAction.TokenServiceImpl;
import lombok.RequiredArgsConstructor;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@RequiredArgsConstructor
public class AuthenticationJWTFilter extends OncePerRequestFilter {

    // Quando a aplicação subir, essa classe vai ser chamada antes do spring security.
    // Nas proximas requisições , o spring security não vai ser chamado mais, pois as informações já serão salvas em memória,
    // porém, esse filtro sera chamado em todas as requisições para recuperar o token, validar se o token está válido...

    private final TokenServiceImpl tokenService;
    private final UserRepository userRepository;


    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) // Método para prosseguir com a requisição!
            throws ServletException, IOException {
        String token = recoverToken(request);
        boolean valid = tokenService.isTokenValid(token); // retorna V/F se o token está valido
        if (valid) { // se o token estiver válido
            recoverUser(token); // autentica o usuário.
        }
        filterChain.doFilter(request, response);
    }


    private void recoverUser(String token) { // Método para recuperar o usuário e valida-lo através do token.
        String idUser = tokenService.getIdUser(token);
        User user = userRepository.findById(idUser).orElseThrow(
                ()->new DatabaseException("Error entering in the system, the token might be invalid"));
        UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities());
        SecurityContextHolder.getContext().setAuthentication(authentication);
    }

    private String recoverToken(HttpServletRequest request) { // Método para recuperar o token
        String token = request.getHeader("Authorization");
        if (token == null || token.isEmpty() || !token.startsWith("Bearer ")) {
            return null;
        }

        return token.substring(7);
    }
}
package application.config.security;

import application.filter.AuthenticationJWTFilter;
import application.repository.UserRepository;
import application.service.serviceAction.TokenServiceImpl;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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.annotation.web.configuration.WebSecurityCustomizer;
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;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

import java.util.Arrays;

@RequiredArgsConstructor
// Faz com que quando a classe for instanciada, os atributos vão ser passados no construtor automaticamente.
@EnableWebSecurity
// Desabilita as configurações default do Spring Security, permitindo a gente á configurar as nossas próprias.
@Configuration  // Indica que é uma classe de configuração
public class SecurityConfigurationsImpl implements SecurityConfigurations { // As classes de Security só são chamadas quando a aplicação sobe!
    // Nas proximas requisições, essa classe não é chamada dnv, pois as conf já estão salvas.

    private final TokenServiceImpl tokenService; // Classe que contém ações de um token como gerar um token...
    private final UserRepository userRepository; // Repositório da entidade Usuário
    @Override
    @Bean
    public PasswordEncoder encoder() {
        return new BCryptPasswordEncoder();
    }
    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {  // AuthManager, Conf de autenticação.
        return authenticationConfiguration.getAuthenticationManager();
    }
    @Override
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { // Configs de Autorização.

        http.authorizeRequests()// Autorização de requests
                .antMatchers("/auth").permitAll() // Estou permitindo TUDO E TODOS acessarem esse recurso no sistema.
                .antMatchers("/userarea/**").authenticated() // Para acessar esse recurso, tem q estar autenticado.
                .antMatchers("/users/register").permitAll() //  Estou permitindo TUDO E TODOS acessarem esse recurso no sistema.
                .antMatchers("/users/**").hasRole("ADMINISTRATOR") // Para acessas esse curso, a pessoa logada tem que ser ADM
                .anyRequest().denyAll() // Qualquer outro recurso, sem ser os de cima, serão bloqueados ( securança pra manutenção). No caso, não tem outros recurso.
                .and().exceptionHandling().authenticationEntryPoint(new UnauthorizedEntryPoint()) // Essa linha vai chamar a classe Unhautorize... para lídar com o erro 401.
                .and().cors() // Libera a integração de aplicações externas como o front-end á essa API.
                .and().headers().frameOptions().disable() // É para bloquear a página de login ser colocada em um iFrame
                .and().csrf().disable() // Comentário sobre essa conf na linha 88.
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) 
                .and().addFilterBefore(new AuthenticationJWTFilter(tokenService, userRepository), UsernamePasswordAuthenticationFilter.class); // Adiciona um FILTRO, Antes
        // dessa classe ser chamada.

        return http.build();
    }
    @Bean
    public WebSecurityCustomizer webSecurityCustomizer() {
        return (web) -> web.ignoring().antMatchers
                ("/swagger-ui/**", "/v3/api-docs/**", "/h2-console/**");
    }
    @Bean
    public CorsConfigurationSource corsConfigurationSource() {           // Método relacionado á CORS, integração com um meio externo.
        CorsConfiguration configuration = new CorsConfiguration().applyPermitDefaultValues();
        configuration.setAllowedMethods(Arrays.asList("POST", "GET", "PUT", "DELETE", "OPTIONS"));
        final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return source;
    }
}

Acho que pode ser sim então essa questão do ID mudar a cada restart, por conta desse trecho no filter:

String idUser = tokenService.getIdUser(token);
User user = userRepository.findById(idUser).orElseThrow(()->new DatabaseException("Error entering in the system, the token might be invalid"));

Como mudou o id, ele não encontra o registro no banco e deve estar lançando essa exception.