javaspringspring-bootspring-mvcspring-security

Springboot not triggering custom authentication for my loaduserbyusername method


I was implementing an authentication mechanism for my app. So, if the user is admin and correct username and password is provided, then user should be authenticated and home page should be displayed to the admin. But in my case, even when correct username and password is provided, even in that case the control is redirected to login page instead of home page.

Below are the further details:-

login page:-

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Login & Registration Form | CoderGirl</title>
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <div class="container">
    <input type="checkbox" id="check">
    <div class="login form">
      <header>Login</header>
      <form action="/home" method="POST">
        <input type="text" name="username" placeholder="Enter your email" required>
        <input type="password" name="password" placeholder="Enter your password" required>
        <a href="#">Forgot password?</a>
        <input type="submit" class="button" value="Login">
      </form>
      <div class="signup">
        <span class="signup">Don't have an account?
         <label for="check">Signup</label>
        </span>
      </div>
    </div>
    <div class="registration form">
      <header>Signup</header>
      <form action="/register" method="POST">
        <input type="text" name="username" placeholder="Enter your email" required>
        <input type="password" name="password" placeholder="Create a password" required>
        <input type="password" name="confirmPassword" placeholder="Confirm your password" required>
        <input type="submit" class="button" value="Signup">
      </form>
      <div class="signup">
        <span class="signup">Already have an account?
         <label for="check">Login</label>
        </span>
      </div>
    </div>
  </div>
</body>
</html>

home page:-

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Product Management</title>
  <link rel="stylesheet" href="home_style.css">
</head>
<body>
  <div class="container">
    <h1>Welcome to the Product Management System</h1>
    <div class="button-container">
      <button class="action-button" onclick="window.location.href='/add-product'">Add Product</button>
      <button class="action-button" onclick="window.location.href='/remove-product'">Remove Product</button>
      <button class="action-button" onclick="window.location.href='/modify-product'">Modify Product</button>
      <button class="action-button" onclick="window.location.href='/view-product'">View Product</button>
    </div>
    <a href="/logout" class="logout-button">Logout</a>
  </div>
</body>
</html>

controller:-

@Controller
public class LoginController {

    @Autowired
    private UserService userService;
    
    @Autowired
    private CustomUserDetailsService customservice;

    @Autowired
    private PasswordEncoder passwordEncoder;
    
    @GetMapping("/login")
    public String loginPage() {
        return "index"; // Render the login.html page
    }
    
    @GetMapping("/logout")
    public String logoutPage() {
        return "index"; // Render the logout.html page
    }

    @PostMapping("/home")
    public String login(@RequestParam String username, @RequestParam String password) {
        // Retrieve user from the database
        System.out.println("login endpoint");
        User user = userService.findByUsername(username);
      
        Iterator<Role> iterator = user.getRoles().iterator();
        while (iterator.hasNext()) {
            Role fruit = iterator.next();
            System.out.println(fruit.getRoleName());
        }
        
        if (user != null && passwordEncoder.matches(password, user.getPassword())) {
            System.out.println("authentication complete");
            return "home";  // Password matches, redirect to home
        } else {
            System.out.println("authentication incomplete");
            return "index";  // Invalid login, return to login page
        }
    }
    }

This is my security config:-

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Autowired
    private CustomUserDetailsService customUserDetailsService; // Inject the custom user details service

    @Bean
    public PasswordEncoder passwordEncoder() {
        System.out.println("inside password encoder");
        return new BCryptPasswordEncoder(); // Passwords should be hashed using BCrypt
    }

    @Bean
    public DaoAuthenticationProvider authenticationProvider() {
        System.out.println("inside authentication provider");
        DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
        authProvider.setUserDetailsService(customUserDetailsService); // Use injected CustomUserDetailsService
        authProvider.setPasswordEncoder(passwordEncoder()); // Set password encoder
        return authProvider;
    }

    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
        System.out.println("inside authentication manager");
        return authenticationConfiguration.getAuthenticationManager();
    }

    
    
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        System.out.println("inside securityFilterChain");
        http
            .authorizeHttpRequests()
            .requestMatchers("/css/**", "/js/**", "/images/**", "/static/**").permitAll()
            .requestMatchers("/login", "/register", "*.css").permitAll() // Public access to login and register
            .requestMatchers("/add-product", "/modify-product", "/remove-product").hasRole("ADMIN") // Restrict product modification to ADMIN roles
            .requestMatchers("/view-product").authenticated() // Requires users to be authenticated
            .anyRequest().authenticated() // All other requests need to be authenticated
            .and()
            .formLogin()
                .loginPage("/login")
                .defaultSuccessUrl("/home", true) // Redirect to home after login
                .failureUrl("/login") 
            .and()
            .logout()
                .logoutSuccessUrl("/logout") // Redirect after logout
                .permitAll()
            .and()
            .sessionManagement() // Configure session management
                .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED) // Default policy
                .maximumSessions(1) // Limit the number of concurrent sessions per user
                .expiredUrl("/login?expired") // Redirect to login page if session expires
            .and()
            .and()
            .csrf().disable(); // Disable CSRF for non-browser clients or API access

        return http.build();
    }
}

custom userdetailservice:-

@Service
public class CustomUserDetailsService implements UserDetailsService {

    @Autowired
    private UserRepository userRepository;

    public CustomUserDetailsService() {
        System.out.println("CustomUserDetailsService has been initialized.");
    }
    
    
    // Map roles to authorities, adding 'ROLE_' prefix
    private Collection<? extends GrantedAuthority> mapRolesToAuthorities(Collection<Role> roles) {
        return roles.stream()
                    .map(role -> new SimpleGrantedAuthority(role.getRoleName()))
                    .collect(Collectors.toList());
    }


    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        System.out.println("inside loaduserbyusername custom user");
        User user = userRepository.findByUsername(username);
        System.out.println("User is "+user.getUsername() +" and password is "+user.getPassword());
        if (user == null) {
            throw new UsernameNotFoundException("User not found");
        }
        org.springframework.security.core.userdetails.User springUser = new org.springframework.security.core.userdetails.User(
                user.getUsername(),
                user.getPassword(),
                mapRolesToAuthorities(user.getRoles())
        );
        System.out.println("Spring Security User Details: ");
        System.out.println("Username: " + springUser.getUsername());
        System.out.println("Password: " + springUser.getPassword());
        System.out.println("Authorities: " + springUser.getAuthorities());
        return springUser;

    }
}

logs:-

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                (v3.1.0)

2024-10-26T20:07:44.105+05:30  INFO 19060 --- [  restartedMain] com.example.webapp.Dia1Application       : Starting Dia1Application using Java 17.0.3 with PID 19060 (C:\Users\Admin\eclipse-workspace\dia-1\target\classes started by Admin in workspace\dia-1)
2024-10-26T20:07:44.162+05:30  INFO 19060 --- [  restartedMain] com.example.webapp.Dia1Application       : No active profile set, falling back to 1 default profile: "default"
2024-10-26T20:07:44.458+05:30  INFO 19060 --- [  restartedMain] o.s.b.devtools.restart.ChangeableUrls    : The Class-Path manifest attribute in C:\Users\Admin\.m2\repository\com\oracle\database\jdbc\ojdbc8\19.8.0.0\ojdbc8-19.8.0.0.jar referenced one or more files that do not exist: file:/C:/Userscom/oracle/database/jdbc/ojdbc8/19.8.0.0/oraclepki.jar
2024-10-26T20:07:44.459+05:30  INFO 19060 --- [  restartedMain] .e.DevToolsPropertyDefaultsPostProcessor : Devtools property defaults active! Set 'spring.devtools.add-properties' to 'false' to disable
2024-10-26T20:07:44.460+05:30  INFO 19060 --- [  restartedMain] .e.DevToolsPropertyDefaultsPostProcessor : For additional web related logging consider setting the 'logging.level.web' property to 'DEBUG'
2024-10-26T20:07:47.352+05:30  INFO 19060 --- [  restartedMain] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JPA repositories in DEFAULT mode.
2024-10-26T20:07:47.948+05:30  INFO 19060 --- [  restartedMain] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 367 ms. Found 3 JPA repository interfaces.
2024-10-26T20:07:51.595+05:30  INFO 19060 --- [  restartedMain] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2024-10-26T20:07:51.642+05:30  INFO 19060 --- [  restartedMain] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2024-10-26T20:07:51.643+05:30  INFO 19060 --- [  restartedMain] o.apache.catalina.core.StandardEngine    : Starting Servlet engine: [Apache Tomcat/10.1.8]
2024-10-26T20:07:51.957+05:30  INFO 19060 --- [  restartedMain] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2024-10-26T20:07:51.959+05:30  INFO 19060 --- [  restartedMain] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 7497 ms
2024-10-26T20:07:52.995+05:30  INFO 19060 --- [  restartedMain] o.hibernate.jpa.internal.util.LogHelper  : HHH000204: Processing PersistenceUnitInfo [name: default]
2024-10-26T20:07:53.414+05:30  INFO 19060 --- [  restartedMain] org.hibernate.Version                    : HHH000412: Hibernate ORM core version 6.2.2.Final
2024-10-26T20:07:53.428+05:30  INFO 19060 --- [  restartedMain] org.hibernate.cfg.Environment            : HHH000406: Using bytecode reflection optimizer
2024-10-26T20:07:54.294+05:30  INFO 19060 --- [  restartedMain] o.h.b.i.BytecodeProviderInitiator        : HHH000021: Bytecode provider name : bytebuddy
2024-10-26T20:07:55.194+05:30  INFO 19060 --- [  restartedMain] o.s.o.j.p.SpringPersistenceUnitInfo      : No LoadTimeWeaver setup: ignoring JPA class transformer
2024-10-26T20:07:55.295+05:30  INFO 19060 --- [  restartedMain] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Starting...
2024-10-26T20:07:56.818+05:30  INFO 19060 --- [  restartedMain] com.zaxxer.hikari.pool.HikariPool        : HikariPool-1 - Added connection oracle.jdbc.driver.T4CConnection@567f88b3
2024-10-26T20:07:56.826+05:30  INFO 19060 --- [  restartedMain] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Start completed.
2024-10-26T20:07:58.454+05:30  INFO 19060 --- [  restartedMain] org.hibernate.orm.dialect                : HHH035001: Using dialect: org.hibernate.dialect.OracleDialect, version: 21.0
2024-10-26T20:07:59.957+05:30  INFO 19060 --- [  restartedMain] o.h.b.i.BytecodeProviderInitiator        : HHH000021: Bytecode provider name : bytebuddy
2024-10-26T20:08:04.301+05:30  INFO 19060 --- [  restartedMain] o.h.e.t.j.p.i.JtaPlatformInitiator       : HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform]
2024-10-26T20:08:04.329+05:30  INFO 19060 --- [  restartedMain] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default'
CustomUserDetailsService has been initialized.
inside password encoder
2024-10-26T20:08:06.758+05:30  WARN 19060 --- [  restartedMain] JpaBaseConfiguration$JpaWebConfiguration : spring.jpa.open-in-view is enabled by default. Therefore, database queries may be performed during view rendering. Explicitly configure spring.jpa.open-in-view to disable this warning
inside authentication provider
inside securityFilterChain
2024-10-26T20:08:07.257+05:30  INFO 19060 --- [  restartedMain] o.s.b.a.w.s.WelcomePageHandlerMapping    : Adding welcome page template: index
2024-10-26T20:08:08.083+05:30  INFO 19060 --- [  restartedMain] o.s.s.web.DefaultSecurityFilterChain     : Will secure any request with [org.springframework.security.web.session.DisableEncodeUrlFilter@7beeb809, org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@1250ad26, org.springframework.security.web.context.SecurityContextHolderFilter@f1c53e8, org.springframework.security.web.header.HeaderWriterFilter@5e21c003, org.springframework.security.web.authentication.logout.LogoutFilter@6af304cf, org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter@47bc56f1, org.springframework.security.web.session.ConcurrentSessionFilter@576c970f, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@2ad56bcf, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@5378e4fd, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@377e7a43, org.springframework.security.web.session.SessionManagementFilter@1454bc00, org.springframework.security.web.access.ExceptionTranslationFilter@7b40484, org.springframework.security.web.access.intercept.AuthorizationFilter@107f250f]
inside authentication manager
2024-10-26T20:08:09.118+05:30  INFO 19060 --- [  restartedMain] o.s.b.d.a.OptionalLiveReloadServer       : LiveReload server is running on port 35729
2024-10-26T20:08:09.257+05:30  INFO 19060 --- [  restartedMain] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2024-10-26T20:08:09.300+05:30  INFO 19060 --- [  restartedMain] com.example.webapp.Dia1Application       : Started Dia1Application in 27.13 seconds (process running for 30.667)
Hibernate: select r1_0.role_id,r1_0.role_name from roles r1_0 where r1_0.role_name=?
Hibernate: select u1_0.user_id,u1_0.password,u1_0.user_name from users u1_0 where u1_0.user_name=?
Hibernate: select r1_0.user_id,r1_1.role_id,r1_1.role_name from user_roles r1_0 join roles r1_1 on r1_1.role_id=r1_0.role_id where r1_0.user_id=?
2024-10-26T20:08:29.660+05:30  INFO 19060 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring DispatcherServlet 'dispatcherServlet'
2024-10-26T20:08:29.661+05:30  INFO 19060 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Initializing Servlet 'dispatcherServlet'
2024-10-26T20:08:29.671+05:30  INFO 19060 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Completed initialization in 7 ms
2024-10-26T20:08:29.708+05:30 DEBUG 19060 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy        : Securing POST /home
2024-10-26T20:08:29.747+05:30 DEBUG 19060 --- [nio-8080-exec-1] o.s.s.w.a.AnonymousAuthenticationFilter  : Set SecurityContextHolder to anonymous SecurityContext
2024-10-26T20:08:29.887+05:30 DEBUG 19060 --- [nio-8080-exec-1] o.s.s.w.s.HttpSessionRequestCache        : Saved request http://localhost:8080/home?continue to session
2024-10-26T20:08:29.892+05:30 DEBUG 19060 --- [nio-8080-exec-1] o.s.s.web.DefaultRedirectStrategy        : Redirecting to http://localhost:8080/login
2024-10-26T20:08:29.974+05:30 DEBUG 19060 --- [nio-8080-exec-2] o.s.security.web.FilterChainProxy        : Securing GET /login
2024-10-26T20:08:29.975+05:30 DEBUG 19060 --- [nio-8080-exec-2] o.s.s.w.a.AnonymousAuthenticationFilter  : Set SecurityContextHolder to anonymous SecurityContext
2024-10-26T20:08:29.981+05:30 DEBUG 19060 --- [nio-8080-exec-2] o.s.security.web.FilterChainProxy        : Secured GET /login
2024-10-26T20:08:31.829+05:30 DEBUG 19060 --- [nio-8080-exec-3] o.s.security.web.FilterChainProxy        : Securing GET /style.css
2024-10-26T20:08:31.829+05:30 DEBUG 19060 --- [nio-8080-exec-3] o.s.s.w.a.AnonymousAuthenticationFilter  : Set SecurityContextHolder to anonymous SecurityContext
2024-10-26T20:08:31.866+05:30 DEBUG 19060 --- [nio-8080-exec-3] o.s.security.web.FilterChainProxy        : Secured GET /style.css

roles table details:-

ROLE_ID   ROLE_NAME
1         ROLE_ADMIN

so, i have added the logs as well inside loaduserbyusername, but those logs are not generating during app startup or on successful login. could anyone help like what's going wrong.


Solution

  • You have an error on the login page in the line <form action="/home" method="POST">. You send authentication data to an endpoint /home that not only can't accept it, but is also protected from anonymous access, so Spring Security redirects you to the login page over and over again.

    You need to send a username and password to the /login.

    <form action="/login" method="POST">
    ...
    </form>