javaspringspring-bootspring-mvcspring-security

My userrepo is null so can't use findbyusername is userdetailsservice


I have injected my userrepo with @Autowired. I can signup and add users to the database without any problem. However when I have to authenticate against users in my database I get this issue

Caused by: java.lang.NullPointerException: Cannot invoke "com.workout.befit.repository.UserRepo.findByUsername(String)" because "this.userrepo" is null
        at com.workout.befit.services.CustomUserDetails.loadUserByUsername(CustomUserDetails.java:29) ~[classes/:na]
        at org.springframework.security.authentication.dao.DaoAuthenticationProvider.retrieveUser(DaoAuthenticationProvider.java:107) ~[spring-security-core-6.4.2.jar:6.4.2]

this is my repo

package com.workout.befit.repository;

import org.springframework.data.mongodb.repository.MongoRepository;

import com.workout.befit.models.User;
import java.util.Optional;


public interface UserRepo extends MongoRepository<User, String> {
    Optional<User> findByUsername(String username);
}

this is my security config

package com.workout.befit.utils;

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.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;

import com.workout.befit.services.CustomUserDetails;


@Configuration
@EnableWebSecurity
@ComponentScan
public class SecurityConfig {
    @Autowired
    @Qualifier("customuserdetails")
    CustomUserDetails customuserdetails;

    @Autowired
    PasswordEncoder passencoder;

    @Autowired
    AuthenticationProvider authprovider;
    

    @Bean
    public SecurityFilterChain securityFilterchain(HttpSecurity http) throws Exception {
        http
        .authorizeHttpRequests(authorize->authorize.requestMatchers("/signup","/about","/error/**").permitAll() //allow requests to signup page
        .anyRequest().authenticated()) //authenticate all other requests
        .httpBasic(Customizer.withDefaults())
        .formLogin(Customizer.withDefaults())
        .csrf(csrf-> csrf.disable());
        return http.build();
    }

    
 

}

this is my security bean

package com.workout.befit.utils;

import org.springframework.context.annotation.Bean;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;

import com.workout.befit.services.CustomUserDetails;

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

    @Bean
    public UserDetailsService customuserdetailsservice(){
        return new CustomUserDetails();
    }

    @Bean
    public AuthenticationProvider authprovider(){
        DaoAuthenticationProvider provider=new DaoAuthenticationProvider();
        provider.setPasswordEncoder(passwordEncoder());
        provider.setUserDetailsService(customuserdetailsservice());
        return provider;
    }
}

this is my custom user details service

package com.workout.befit.services;

import java.util.Optional;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Primary;
import org.springframework.security.core.userdetails.User.UserBuilder;
import org.springframework.security.crypto.password.PasswordEncoder;
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 com.workout.befit.models.User;
import com.workout.befit.repository.UserRepo;

@Service("customuserdetails")
public class CustomUserDetails implements UserDetailsService {

    @Autowired
    UserRepo userrepo;
    @Autowired
    PasswordEncoder bcrypt;


    @SuppressWarnings("null")
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {   
        Optional<User> optionaluser=userrepo.findByUsername(username);
        User user=optionaluser.orElseThrow();
        UserBuilder builder = null;
        builder.username(user.getUsername());
        builder.password(bcrypt.encode(user.getPassword()));
        builder.authorities(user.getAuthorities());
        return builder.build();
    }

    
}

It says that this.userrepo in customuserdetailsservice is null therefore it can't call find by name function.


Solution

  • Try adding this in your code instead of using field level bean creation, use constructor based bean creation for the UserRepo in CustomerDetails class like mentioned by @M.Deinum

    import lombok.RequiredArgsConstructor;
    
    @Service("customuserdetails")
    @RequiredArgsConstructor
    public class CustomUserDetails implements UserDetailsService {
    
        private final UserRepo userrepo;
        private final PasswordEncoder bcrypt;
    // rest of the code
    }
    

    and add the @Configuration annotation instead of @Component to SecurityBeans class

    import org.springframework.context.annotation.Configuration;
    
    @Configuration
    public class Securitybeans {
    
        //rest of the code
    }