javaspringspring-securityuserdetailsservice

Custom UserDetailsService Impl not being Called in SpringSecurity


My Custom UserDetailsService

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.User;
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;
import org.springframework.transaction.annotation.Transactional;

import br.com.fimd.entidades.Usuario;
import br.com.fimd.service.UsuarioService;

@Transactional(readOnly=true)
@Service("userDetailsServiceImpl")
public class UserDetailsServiceImpl implements UserDetailsService{

@Autowired
private UsuarioService usuarioService;

@Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
    Usuario usuario = usuarioService.findUserByEmail(email);
    if(usuario == null) {
        throw new UsernameNotFoundException("Usuário nao encontrado.");
    }
    return new User(usuario.getEmail(), usuario.getSenha(), usuario.getAuthorities());
}

}

My Security Configuration

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

@Autowired
@Qualifier("userDetailsServiceImpl")
private UserDetailsService userDetailsService;

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {    
    auth.userDetailsService(userDetailsService).passwordEncoder(new BCryptPasswordEncoder());
}

@Override
protected void configure(HttpSecurity http) throws Exception {

    http.cors().disable().
    csrf().disable().authorizeRequests()
    .antMatchers("/home").permitAll()
    .antMatchers("/public/**").permitAll()
    .antMatchers("/private/**").hasRole("USER")
    //.antMatchers("/admin*").access("hasRole('ROLE_ADMIN')")
    .antMatchers("/admin/**").hasRole("ADMIN")
    .anyRequest().authenticated()       
    .and()
    //.addFilterBefore(new JWTAuthenticationFilter(),UsernamePasswordAuthenticationFilter.class)
    .logout().logoutRequestMatcher(new AntPathRequestMatcher("/logout"));

}

@Override
public void configure(WebSecurity web) throws Exception {
    web.ignoring().antMatchers("/assets/**");
}

@Bean
CorsConfigurationSource corsConfigurationSource() {
    UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    source.registerCorsConfiguration("/**", new CorsConfiguration().applyPermitDefaultValues());
    return source;
}

}

My User Entity

import java.io.Serializable;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EntityListeners;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.validation.constraints.NotBlank;

import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;

@Entity
@Table(name = "usuarios")
@EntityListeners(AuditingEntityListener.class)
@JsonIgnoreProperties(value = { "createdAt", "updatedAt" }, allowGetters = true)
public class Usuario implements UserDetails, Serializable{

private static final long serialVersionUID = 4038437690572999966L;

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

@NotBlank
private String nome;

@NotBlank
private String email;

@NotBlank
private String senha;

@NotBlank
private String cpf;

@Column
private boolean ativo;

@ManyToMany
 @JoinTable(name="perfil_usuario", joinColumns=
    {@JoinColumn(name="user_id")}, inverseJoinColumns=
    {@JoinColumn(name="tipo_perfil")})
private Set<Perfil> perfis = new HashSet<>();

@OneToMany(mappedBy = "usuario", cascade = CascadeType.ALL)
private List<Investimento> investimentos;

@OneToMany(mappedBy = "usuario", cascade = CascadeType.ALL)
private List<Extrato> extratos;

public Long getId() {
    return id;
}

public void setId(Long id) {
    this.id = id;
}

public String getNome() {
    return nome;
}

public void setNome(String nome) {
    this.nome = nome;
}

public String getEmail() {
    return email;
}

public void setEmail(String email) {
    this.email = email;
}

public String getCpf() {
    return cpf;
}

public void setCpf(String cpf) {
    this.cpf = cpf;
}

public static long getSerialversionuid() {
    return serialVersionUID;
}

public String getSenha() {
    return senha;
}

public void setSenha(String senha) {
    this.senha = senha;
}

public boolean isAtivo() {
    return ativo;
}

public void setAtivo(boolean ativo) {
    this.ativo = ativo;
}

public Set<Perfil> getPerfis() {
    return perfis;
}

public void setPerfis(Set<Perfil> perfis) {
    this.perfis = perfis;
}

public List<Investimento> getInvestimentos() {
    return investimentos;
}

public void setInvestimentos(List<Investimento> investimentos) {
    this.investimentos = investimentos;
}

public List<Extrato> getExtratos() {
    return extratos;
}

public void setExtratos(List<Extrato> extratos) {
    this.extratos = extratos;
}

@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
    return this.perfis;
}

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

@Override
public String getUsername() {
    return this.email;
}

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

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

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

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

}

My Perfil Entity

import java.util.List;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.ManyToMany;

import org.springframework.security.core.GrantedAuthority;

@Entity
public class Perfil implements GrantedAuthority{

private static final long serialVersionUID = 1L;

@Id
private String tipoPerfil;

@ManyToMany(mappedBy="perfis")
private List<Usuario> usuarios;

public List<Usuario> getUsuarios() {
    return usuarios;
}

public void setUsuarios(List<Usuario> usuarios) {
    this.usuarios = usuarios;
}

public String getTipoPerfil() {
    return tipoPerfil;
}

public void setTipoPerfil(String tipoPerfil) {
    this.tipoPerfil = tipoPerfil;
}

@Override
public String getAuthority() {
    return this.tipoPerfil;
}

}

My Spring Filter Initializer

import org.springframework.context.annotation.Configuration;
import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;

@Configuration
public class SecurityInitializer extends AbstractSecurityWebApplicationInitializer {

}

When i call a "/private/service" my loadUserByUsername not being called never, my application ever send me a 403 forbidden, access denied. Why that? I tried a lot of tutorials from internet and my code seems to be correct, i'm using spring starter 2.0.5.RELEASE

I already tried to call the userDetailsService from a @Bean method in SecurityConfiguration


Solution

  • Your security configuration is not complete, you must provide as well the login information:

    .formLogin()
    .loginPage("/login.html")
    .defaultSuccessUrl("/home")
    .failureUrl("/login.html?error=true")
    

    Otherwise all your requests will be unauthorized. Please see this tutorial:

    https://www.baeldung.com/spring-security-login

    Your Custom UserDetails service will be called only when the application will receive a POST to login url with username and password, otherwise it will not have what data to authorize.