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

Erro na lógica de login

Estou com o seguinte problema, ao passar os dados corretos do login e senha da tudo certo e retorna o token e tals, porém ao digitar login e senha errados ao invés de gerar uma mensagem de erro, o programa ta dando 403 forbidden, segue os códigos abaixo:

Controller:

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

    @Autowired
    private AuthenticationManager manager;

    @Autowired
    private TokenService tokenService;

    @PostMapping
    public ResponseEntity efetuarLogin(@RequestBody @Valid DadosAutenticaoDto dados) {
        var authenticationToken = new UsernamePasswordAuthenticationToken(dados.login(), dados.senha());
        var authentication = manager.authenticate(authenticationToken);

        var tokenJwt = tokenService.gerarToken((Usuario) authentication.getPrincipal());
        return ResponseEntity.ok(new DadosTokenJwtDto(tokenJwt));
    }
}

Service:

@Service
public class AutenticacaoService implements UserDetailsService {

    private final UsuarioRepository repository;

    public AutenticacaoService(UsuarioRepository repository) {
        this.repository = repository;
    }

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

Model:


@Data
@Entity
@Table(name = "usuarios")
public class Usuario 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;
    }
}

SecurityFilter:

@Component
public class SecurityFilter extends OncePerRequestFilter {

    @Autowired
    private TokenService tokenService;

    @Autowired
    private UsuarioRepository usuarioRepository;

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

        var tokenJWT = recuperarToken(request);

        if(tokenJWT != null) {
            var subject = tokenService.getSubject(tokenJWT);
            var usuario = usuarioRepository.findByLogin(subject);
            var authentication = new UsernamePasswordAuthenticationToken(usuario, null, usuario.getAuthorities());
            SecurityContextHolder.getContext().setAuthentication(authentication);
        }

        filterChain.doFilter(request, response);
    }

    private String recuperarToken(HttpServletRequest request) {
        var authorizationHeader = request.getHeader("Authorization");

        if(authorizationHeader != null) {
            return authorizationHeader.replace("Bearer ", "").trim();
        }

        return null;
    }
}

SecurityConfigurations:

@Configuration
@EnableWebSecurity
public class SecurityConfigurations {

    @Autowired
    private SecurityFilter securityFilter;

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        return http.csrf(AbstractHttpConfigurer::disable)
                .sessionManagement(sm -> sm.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                .authorizeHttpRequests(req -> {
                    req.requestMatchers(HttpMethod.POST, "api/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();
    }
}
8 respostas

Oi!

O Spring devolve erro 403 mesmo quando a autenticação falha. Se quiser tratar de maneira diferente, veja nessa atividade: https://cursos.alura.com.br/course/spring-boot-aplique-boas-praticas-proteja-api-rest/task/125341

Bons estudos!

fala rodrigo, boa noite. depois de umas pesquisas eu percebi isso kkkkk mas agora tô tendo um problema meio estranho que nunca aconteceu em apis antes desenvolvida por mim, estou tentando fazer a função de login e senha em meu front end e da erro 403 forbidden, apenas para o método de login. os outros métodos eu passo um token válido que pego no insomnia e funciona, sabe se tem alguma explicacao pra isso ? (meu cors está configurado certinho creio eu)

Cors:

package com.gpf.ti.infra.security;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class CorsConfiguration implements WebMvcConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedOrigins("http://localhost:3000")
                .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS", "HEAD", "TRACE", "CONNECT");
    }
}

meu front:

<template>
    <main>
        <h1>Login</h1>
        <form @submit.prevent="efetuarLogin">
            <label for="usuario">Usuario</label>
            <input type="text" name="usuario" id="usuario" v-model="usuario">
            <label for="senha">senha</label>
            <input type="text" name="senha" id="senha" v-model="senha">
            <button type="submit">Login</button>
        </form>
    </main>
</template>

<script lang="ts">
import axios from "axios"

export default {
    data() {
        return {
            usuario: "",
            senha: ""
        }
    },
    methods: {
        efetuarLogin() {
            const dados = {
                usuario: this.usuario,
                senha: this.senha
            }

            axios.post("http://localhost:8080/api/login", dados)
                .then((response) => {
                    console.log(response);
                })
                .catch((error) => {
                    console.log(error);
                })
        }
    }
}
</script>

Verifica no backend se está chegando o usuario/senha certinho, conforme enviado do frontend. Pode ser que no frontend tenha algum problema e esteja chegando null no backend, por exemplo.

vou fazer isso sim, no back ainda nao verifiquei mas no console.log do front, consigo ver que os dados foram enviados corretamente, vou ver se consigo resolver aqui, obrigado.

Acho que o problema é que no frontend o seu campo se chama usuario, mas no backend está como login no record DadosAutenticaoDto.

solução!
const dados = {
    usuario: this.usuario,
    senha: this.senha
}

Deveria ser:

const dados = {
    login: this.usuario,
    senha: this.senha
}

puts, bem lembrado, vou testar é ja isso

funcionou kkkkkkk, cabeça esta a milhao hoje que deixei passar essa kkkkk, obrigado Rodrigo!