6
respostas

Erro java.lang.StackOverflowError: null

Estou em um projeto a qual estou me baseando a autenticação a que foi feita no curso, no caso com sua variação de duas entidades e com a versão do spring 3.0.5

Seguindo os passos do curso, recebi a exception java.lang.StackOverflowError: null ele dá erro no AutenticaçãoController

var authentication = manager.authenticate(token);

parece que o AuthenticationManager não é injetado pelo anotação @Autowired segue o erro abaixo:

2023-06-27T10:33:36.642-03:00 ERROR 12809 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Handler dispatch failed: java.lang.StackOverflowError] with root cause

java.lang.StackOverflowError: null
    at java.base/java.lang.Exception.<init>(Exception.java:103) ~[na:na]
    at java.base/java.lang.ReflectiveOperationException.<init>(ReflectiveOperationException.java:90) ~[na:na]
    at java.base/java.lang.reflect.InvocationTargetException.<init>(InvocationTargetException.java:67) ~[na:na]
    at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:119) ~[na:na]
    at java.base/java.lang.reflect.Method.invoke(Method.java:577) ~[na:na]
    at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:343) ~[spring-aop-6.0.7.jar:6.0.7]

Segue abaixo as classes AutentiçãoController:

@RestController
@RequestMapping("/login")
public class AutenticacaoController {

    @Autowired
    private AuthenticationManager manager;

    @PostMapping
    public ResponseEntity efetuarLoginCliente(@RequestBody @Valid DadosAutenticacao dados){
        var token = new UsernamePasswordAuthenticationToken(dados.login(), dados.senha());
        System.out.println("passei aqui??");

        var authentication = manager.authenticate(token);

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

Os Models Cliente e Funcionário com UserDetails implementado.

@Entity(name = "clientes")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(of="id")
public class Cliente implements UserDetails {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String login;
    private String senha;


    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return List.of(new SimpleGrantedAuthority("ROlE_USER"));
    }

    @Override
    public String getPassword() {
        return senha;
    }

    @Override
    public String getUsername() {
        return login;
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return true;
    }
}

@Table(name="funcionarios")
@Entity(name = "Funcionarios")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(of="id")
public class Funcionario implements UserDetails {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String login;
    private String senha;

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return List.of(new SimpleGrantedAuthority("ROLE_USER"));
    }

    @Override
    public String getPassword() {
        return senha;
    }

    @Override
    public String getUsername() {
        return login;
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return true;
    }
}

e por fim a classe SecurityConfigurations, já que através do método authenticationManager que vai ser gerado o AuthenticationManager lá para o controller

@Configuration
@EnableWebSecurity
public class SecurityConfigurations {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception{
        return http.csrf().disable()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and().build();

    }

    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration configuration) throws  Exception{
        return configuration.getAuthenticationManager();
    }

    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }
}

Obs: fiz apenas o login para o cliente, ainda não fiz a para o funcionário.

6 respostas

Oi!

A princípio não tem erros nos códigos. Manda aqui os imports das classes AutenticacaoController e SecurityConfigurations

Vou mandar a classe completa. Segue abaixo:

SecurityConfigurations

package com.pessoaDeon.config.security;

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.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
@EnableWebSecurity
public class SecurityConfigurations {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception{
        return http.csrf().disable()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and().build();

    }

    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration configuration) throws  Exception{
        return configuration.getAuthenticationManager();
    }

    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }
}

AutenticacaoController

package com.pessoaDeon.api.controller.seguranca;

import com.pessoaDeon.domain.model.dto.seguranca.DadosAutenticacao;
import com.pessoaDeon.domain.model.dto.seguranca.DadosAutenticacaoClienteDTO;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/login")
public class AutenticacaoController {

    @Autowired
    private AuthenticationManager manager;
//    private final AuthenticationManager manager;
//
//    @Autowired
//    public AutenticacaoController(AuthenticationManager manager){
//        this.manager = manager;
//    }
    @PostMapping
    public ResponseEntity efetuarLoginCliente(@RequestBody @Valid DadosAutenticacao dados){
        var token = new UsernamePasswordAuthenticationToken(dados.login(), dados.senha());
        System.out.println("passei aqui??");

        var authentication = manager.authenticate(token);

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

Até tentei injetar pelo lombok, e não deu certo.

Blz, está tudo certo.

Você já criou a classe AutenticacaoService? Sem ela não vai funcionar.

Sim criei, mas como são duas entidades diferentes separei o código. ClienteService

package com.pessoaDeon.domain.service;

;
import com.pessoaDeon.domain.repository.usuarios.ClienteRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

@Service
public class ClienteService implements UserDetailsService {

    @Autowired
    private ClienteRepository clienteRepository;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        return clienteRepository.findByLogin(username);
    }
}

FuncionarioService

package com.pessoaDeon.domain.service;

import com.pessoaDeon.domain.repository.usuarios.FuncionarioRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

@Service
public class FuncionarioService implements UserDetailsService {

    @Autowired
    private FuncionarioRepository funcionarioRepository;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        return funcionarioRepository.findByLogin(username);
    }
}

Como são duas entidades separei o serviços, deixei o ClienteService e FuncionarioService, já implementando UserDetailsService nas duas classes.

Hum, acho que esse é o problema então. Só pode ter uma classe AutenticaoService. Apaga uma delas e teste novamente para verificar se o erro não ocorre mais.

Opa Rodrigo, acabei de testar aqui isso e realmente funcionou a exclusão de uma das classes.

Então o UserDetailsService só pode ser utilizado apenas uma vezes, mesmo sendo interface?