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

Minha rota de login não esta sendo liberada

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

}

@Service
public class TokenService {

    @Value("${api.security.token.secret}")
    private String secret;

    public String generationToken(User user){
        try {
            var algoritmo = Algorithm.HMAC256(secret);
            return JWT.create()
                    .withIssuer("API SpringBoot meu")
                    .withSubject(user.getLogin())
                    .withClaim("ID", user.getId())
                    .withExpiresAt(dataExpiracao())
                    .sign(algoritmo);
        } catch (JWTCreationException exception){
            throw  new RuntimeException("Error ao gerar o token jwt", exception);
        }
    }

    public  String getSubject(String tokenJWT){
        try {
            var algoritmo = Algorithm.HMAC256(secret);
            return JWT.require(algoritmo)
                    .withIssuer("API SpringBoot meu")
                    .build()
                    .verify(tokenJWT)
                    .getSubject();
        } catch (JWTVerificationException exception){
            throw new RuntimeException("Token invalido ou expirado");
        }
    }


    private Instant dataExpiracao() {
        return LocalDateTime.now().plusHours(5).toInstant(ZoneOffset.of("-03:00"));
    }

}
@Component
public class SecurityFilter extends OncePerRequestFilter {

     @Autowired
     private TokenService tokenService;

     @Autowired
     private UserRepository userRepository;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        System.out.println("Disparo de requetoion");
        var tokenJWT = recuperarToken(request);
        System.out.println(tokenJWT);
        if(tokenJWT != null){
            var subject = tokenService.getSubject(tokenJWT);
            System.out.println(subject);
            System.out.println(tokenJWT);
            var userLogin = userRepository.findByLogin(subject);

            var autheticationa = new UsernamePasswordAuthenticationToken(userLogin, null, userLogin.getAuthorities());
            SecurityContextHolder.getContext().setAuthentication(autheticationa);
            System.out.println("testeeeee if do token");
        }


        filterChain.doFilter(request, response);
    }

    private String recuperarToken(HttpServletRequest request) {
        var authorizationHeader = request.getHeader("Authorization");
        if(authorizationHeader !=  null){
            return authorizationHeader.replace("Bearer ", "").trim();
        }
        throw new RuntimeException("Token não enviado no cabeçario Authorization");

    }
}
2024-04-05T19:17:08.679-03:00  INFO 20312 --- [nio-8081-exec-1] o.s.web.servlet.DispatcherServlet        : Completed initialization in 1 ms
Disparo de requetoion
2024-04-05T19:17:08.698-03:00 ERROR 20312 --- [nio-8081-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception

java.lang.RuntimeException: Token não enviado no cabeçario Authorization
    at projeto.crud.joao.com.apicrud.infra.security.SecurityFilter.recuperarToken(SecurityFilter.java:51) ~[classes/:na]
    at projeto.crud.joao.com.apicrud.infra.security.SecurityFilter.doFilterInternal(SecurityFilter.java:29) ~[classes/:na]

Não era para aparecer esse erro visto que estou tentando fazer login com a rota http://localhost:8081/login

3 respostas

Parece que você está trabalhando com Spring Security e JWT para autenticação em uma aplicação Spring Boot. O código fornecido define a configuração de segurança, o serviço para geração e validação de tokens JWT e um filtro de segurança personalizado que é executado em cada requisição para verificar a validade do token JWT.

O erro "Token invalido ou expirado" é lançado quando a tentativa de validar o token JWT falha. Isso pode acontecer por várias razões, incluindo, mas não limitado a, um token que realmente expirou, um token que foi malformado ou um token que foi assinado com uma chave secreta diferente da esperada pelo servidor.

Aqui estão alguns passos que você pode seguir para solucionar o problema:

  1. Verifique a Chave Secreta: Certifique-se de que a chave secreta usada para assinar o token no momento da criação é a mesma usada para verificar o token. Qualquer discrepância entre essas chaves resultará em falha na verificação.

  2. Analisar o Token JWT: Use ferramentas online como o jwt.io para decodificar o token e verificar os campos exp para a data de expiração e iss para o emissor. Isso pode ajudar a identificar se o token está realmente expirado ou se houve um problema na geração do token.

  3. Tratamento de Exceção no Filtro: No método doFilterInternal, você está lançando uma RuntimeException se o cabeçalho de autorização não for encontrado. Embora isso seja útil para depuração, você pode considerar enviar uma resposta HTTP apropriada (como 401 Não Autorizado) diretamente do filtro em vez de lançar uma exceção não tratada que resulta em erro 500.

  4. Logs de Depuração: Você já está imprimindo algumas informações úteis no console. Certifique-se de verificar esses logs para ver o que está sendo passado e recebido em várias etapas da autenticação.

  5. Configuração do Filtro: Garanta que o filtro está sendo registrado e executado na ordem correta dentro da cadeia de filtros do Spring Security. A posição do filtro antes do UsernamePasswordAuthenticationFilter parece correta, mas vale a pena revisar se há outros filtros que possam estar interferindo.

  6. Verifique o Tempo de Expiração: O método dataExpiracao() define o token para expirar 5 horas após a geração. Certifique-se de que o sistema cliente e o servidor estão sincronizados em termos de fuso horário e que o token não está sendo verificado após a expiração.

Se você precisar de mais ajuda para debugar ou tiver dúvidas específicas sobre qualquer parte do código, fique à vontade para perguntar!

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

Creio que não tenha entendido minha dúvida, o erro ocorre em uma rota que não há token, no caso é a rota de login. Não tem como gerar um token para a rota de login, visto que é essa rota que gera o token para acessar as demais rotas.

Portanto, ao chamar a rota de login, o retorno é 403. No entanto, não era para retornar isso, já que ela deveria ser liberada. Não deve ser enviado um token no cabeçalho dessa rota, mas mesmo assim, ela passa pelo filtro que criei na aula.

eu adicionei esse trecho no codigo e deu certo, porem acredito que não seja a maneira correta de fazer

  if (request.getRequestURI().equals("/login")) {
            filterChain.doFilter(request, response);
            return;
        }
   @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
        if (request.getRequestURI().equals("/login")) {
            filterChain.doFilter(request, response);
            return;
        }
        System.out.println("MEU FILTRO");
        var tokenJWT = recuperarToken(request);
        System.out.println(tokenJWT);
        if(tokenJWT != null){
            var subject = tokenService.getSubject(tokenJWT);
            System.out.println(subject);
            System.out.println(tokenJWT);
            var userLogin = userRepository.findByLogin(subject);

            var autheticationa = new UsernamePasswordAuthenticationToken(userLogin, null, userLogin.getAuthorities());
            SecurityContextHolder.getContext().setAuthentication(autheticationa);
            System.out.println("testeeeee if do token");
        }


        filterChain.doFilter(request, response);
    }
solução!

Oi!

Nesse seu método:

private String recuperarToken(HttpServletRequest request) {
    var authorizationHeader = request.getHeader("Authorization");
    if(authorizationHeader !=  null){
        return authorizationHeader.replace("Bearer ", "").trim();
    }
    throw new RuntimeException("Token não enviado no cabeçario Authorization");

}

Você está lançando exception se o token não for enviado. Dessa forma, você está configurando para que o token sempre seja obrigatório, mas não deve ser assim, pois na requisição de efetuar login o token não será enviado, pois ela é a requisição para se obter um token.

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