Search code examples
javaspringmavenspring-securitydeprecated

Moving SpringSecurityConfig to Spring Boot 3


I've been struggling to map SecurityConfig file to the newer Spring version. Specifically, the problem (I think) is caused by the UserDetailsService() or something related to it. I'm trying to log in by passing raw password to the login page while encrypted password is hardcoded into the database (I have just pasted the string returned by the encode() method). While trying to do that, I'm getting following message:

Hibernate: select u1_0.id,u1_0.name,u1_0.password,u1_0.username from users u1_0 where u1_0.username=?
Hibernate: select a1_0.user_id,a1_0.id,a1_0.authority from authority a1_0 where a1_0.user_id=?

It looks like id is not passed correctly somewhere but I'm not sure how to feel about it. I just can't log in properly.

Below I'll show you what I'm coming from and what do i have atm, as well as some other files, however they should be fine since there were rather not too many changes to the way they are coded in Spring Boot 3. Anyway, since I'm not sure they might come to be helpful, so here they are:

Old file:

@Configuration
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
  
  @Autowired
  private UserDetailsService userDetailsService;

  @Bean
  public PasswordEncoder getPasswordEncoder() {
    return new BCryptPasswordEncoder();
  }
  
  @Override
  protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth
      .userDetailsService(userDetailsService)
      .passwordEncoder(getPasswordEncoder());
        
//    auth.inMemoryAuthentication()
//        .passwordEncoder(getPasswordEncoder())
//        .withUser("trevor@craftycodr.com")
//        .password(getPasswordEncoder().encode("asdfasdf"))
//        .roles("USER");
  }
  
  @Override
  protected void configure(HttpSecurity http) throws Exception {
    http
        .authorizeRequests()
          .antMatchers("/").permitAll()
          .anyRequest().hasRole("USER").and()
        .formLogin()
          .loginPage("/login")
          .defaultSuccessUrl("/dashboard")
          .permitAll()
          .and()
        .logout()
          .logoutUrl("/logout")
          .permitAll();
  }
}

New file:

@Configuration
@EnableWebSecurity
public class WebSecurityConfig
{
    @Autowired
    private UserDetailsService userDetailsService;

    @Bean
    public UserDetailsService userDetailsService()
    {
        return new UserDetailsServiceImpl();
    }

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

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception
    {
        return
            http.authorizeHttpRequests()
                .requestMatchers("/").permitAll()
                .anyRequest().hasRole("USER")
                .and()
                .formLogin()
                    .loginPage("/login")
                    .defaultSuccessUrl("/dashboard")
                    .permitAll()
                    .and()
                .logout()
                    .logoutUrl("/logout").permitAll()
                .and().build();
    }

        @Bean
        public AuthenticationProvider authenticationProvider()
        {
            DaoAuthenticationProvider authenticationProvider=new DaoAuthenticationProvider();
            authenticationProvider.setUserDetailsService(userDetailsService());
            authenticationProvider.setPasswordEncoder(passwordEncoder());
            return authenticationProvider;
        }
}

UserDetailsServiceImpl:


@Service
public class UserDetailsServiceImpl implements UserDetailsService
{
    @Autowired
    private UserRepository userRepo;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException
    {
        User user = userRepo.findByUsername(username);

        if(user == null)
        {
            throw new UsernameNotFoundException("Invalid username and password");
        }
        return new CustomSecurityUser(user);
    }
}

CustomSecurityUser:


public class CustomSecurityUser extends User implements UserDetails
{
    private static final long serialVersionUID = 464958176L;
    
    public CustomSecurityUser()
    {}

    public CustomSecurityUser(User user)
    {
        this.setAuthorities(user.getAuthorities());
        this.setId(user.getId());
        this.setName(user.getName());
        this.setPassword(user.getPassword());
        this.setUsername(user.getUsername());
    }

    @Override
    public Set<Authority> getAuthorities()
    {
        return super.getAuthorities();
    }

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

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

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

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

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

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

UserRepository:

public interface UserRepository extends JpaRepository<User, Long>
{
    User findByUsername(String username);
    
}

Authority:

package com.freshvotes.security;
import org.springframework.security.core.GrantedAuthority;
import com.freshvotes.domain.User;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.ManyToOne;

@Entity
public class Authority implements GrantedAuthority
{
    private static final long serialVersionUID = -6420181486L;
    private Long id;
    private String authority;
    private User user;
    @Override
    public String getAuthority()
    {
        return this.authority;
    }
    public void setAuthority(String authority)
    {
        this.authority = authority;
    }

    @Id @GeneratedValue(strategy=GenerationType.IDENTITY)
    public Long getId()
    {
        return this.id;
    }

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

    @ManyToOne()
    public User getUser()
    {
        return this.user;
    }

    public void setUser(User user)
    {
        this.user = user;
    }

}

User entity:

@Entity
@Table(name = "users")
public class User
{
    private Long id;
    private String username;
    private String password;
    private String name;
    private Set<Authority> authorities = new HashSet<>();

    @Id @GeneratedValue(strategy=GenerationType.IDENTITY)
    public Long getId()
    {
        return id;
    }
    public void setId(Long id)
    {
        this.id = id;
    }

    public String getUsername()
    {
        return username;
    }
    public void setUsername(String username)
    {
        this.username = username;
    }

    public String getPassword()
    {
        return password;
    }
    public void setPassword(String password)
    {
        this.password = password;
    }

    public String getName()
    {
        return name;
    }
    public void setName(String name)
    {
        this.name = name;
    }

    @OneToMany(cascade=CascadeType.ALL, fetch=FetchType.EAGER, mappedBy="user")
    public Set<Authority> getAuthorities()
    {
        return this.authorities;
    }
    public void setAuthorities(Set<Authority> authorities)
    {
        this.authorities = authorities;
    }
    

}

I hope it'll be possible to make something out of it. I deleted the imports because i couldn't post my question otherwise.


Solution

  • It appears that there might be an issue with the data in your database. I cloned your repository and conducted a test by adding data, and it seems to be functioning correctly on my end.

    To troubleshoot, try using the following credentials on the login page: Username - user and Password - password.

    Additionally, here are the details of the data I inserted into the database:

    User Table:

    INSERT INTO users(id, name, password, username) values (1, 'kc', '$2a$10$C6454pQIMqMNW76.M8NjYOzvniTgqdl8unnjnWNnMXmkGqE1M9CSC', 'user');
    

    Authority Table:

    INSERT INTO authority(id, authority, user_id) VALUES (1, 'ROLE_USER', 1);
    

    If you're new to this, I recommend adding the following lines to your application properties file. This will enable detailed logging for Spring Security, helping you to trace and debug potential issues:

    Add the following line to your application.properties file:

    logging.level.org.springframework.security=TRACE
    

    Additionally, in your WebSecurityConfig, you can enable debugging by adding debug = true within the @EnableWebSecurity annotation. Here's an example:

    @EnableWebSecurity(debug = true)
    public class WebSecurityConfig {
        // Your security configuration code here
    }