Open In App

Spring Security Custom AuthenticationFailureHandler

Last Updated : 03 Mar, 2024
Improve
Improve
Like Article
Like
Save
Share
Report

In Java, Spring Security is a very powerful framework that can provide comprehensive security services for Java enterprise software applications. One of the essential aspects of the security is authentication and it can be users are verified before granting access to the resource. Spring Security can allow for the customization of the authentication processes, and it can include handling authentication failures.

Implementation of Custom AuthenticationFailureHandler in Spring Security

We can develop the simple login management Spring application with the Custom Authentication failure handler mechanism.

Step 1: We can create the spring project including the below dependencies into the project.

Dependencies:

  • Spring Web
  • Spring Security
  • Spring Data for MongoDB
  • Spring Dev Tools
  • Lombok

Once create the spring project with the above dependencies into the project then file structure looks like the below image.

File Structure:

Project Structure

Step 2: Open the application.properties file and put the below code for the database configuration.

spring.data.mongodb.uri=mongodb://localhost:27017/CustomData

Step 3: Create the new package named as the model and create the java class named as the User.

Go to src > com.gfg.customauthenticationfailure > model > User and put the below code.

Java




package com.gfg.customauthenticationfailure.model;
  
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
  
// The @Data annotation generates getters, setters, toString, equals, and hashCode methods automatically
@Data
// The @AllArgsConstructor annotation generates a constructor with all arguments
@AllArgsConstructor
// The @NoArgsConstructor annotation generates a constructor with no arguments
@NoArgsConstructor
// The @Document annotation marks this class as a document in a MongoDB collection
@Document(collection = "users")
public class User {
    // The @Id annotation marks this field as the primary key of the document
    @Id
    private String id;
    // The @Field annotation specifies the name of the field in the MongoDB document
    // If not specified, the field name will be the same as the variable name
    private String username;
    private String password;
}


Step 4: Create the new package named as the repository and create the java interface named as the UserRepository.

Go to src > com.gfg.customauthenticationfailure > repository > UserRepository and put the below code.

Java




package com.gfg.customauthenticationfailure.repository;
  
import com.gfg.customauthenticationfailure.model.User;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.stereotype.Repository;
  
// The @Repository annotation marks this interface as a repository component in Spring
@Repository
public interface UserRepository extends MongoRepository<User, String> {
  
    // Method to find a user by their username
    User findByUsername(String username);
}


Step 5: Create the new package named as the service and create the java interface named as the UserService.

Go to src > com.gfg.customauthenticationfailure > service > UserService and put the below code.

Java




package com.gfg.customauthenticationfailure.service;
  
import com.gfg.customauthenticationfailure.model.User;
import com.gfg.customauthenticationfailure.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
  
import java.util.List;
  
// The @Service annotation marks this class as a service component in Spring
@Service
public class UserService {
  
    // Autowire the UserRepository to interact with the database
    @Autowired
    private UserRepository userRepository;
  
    // Method to find a user by their username
    public User findByUsername(String username) {
        // Call the findByUsername method of the userRepository to find the user by username
        return userRepository.findByUsername(username);
    }
}


Step 6: Create the new package named as the securityconfig and create the java interface named as the CustomAuthenticationFailureHandler.

Go to src > com.gfg.customauthenticationfailure > securityconfig > CustomAuthenticationFailureHandler and put the below code.

Java




package com.gfg.customauthenticationfailure.securityconfig;
  
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
  
import java.io.IOException;
  
// Implement the AuthenticationFailureHandler interface to create a custom authentication failure handler
public class CustomAuthenticationFailureHandler implements AuthenticationFailureHandler {
  
    // Override the onAuthenticationFailure method to handle authentication failure
    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
        // Your custom failure handling logic goes here
  
        // For example, redirect the user to the login page with an error message in the URL
        response.sendRedirect("/login?error=true");
    }
}


Step 7: Create the new package named as the securityconfig and create the java interface named as the SecurityConfig.

Go to src > com.gfg.customauthenticationfailure > securityconfig > SecurityConfig and put the below code.

Java




package com.gfg.customauthenticationfailure.securityconfig;
  
import com.gfg.customauthenticationfailure.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
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.configuration.EnableWebSecurity;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
  
  
@Configuration
@EnableWebSecurity
public class SecurityConfig {
  
    @Autowired
    private UserRepository userRepository;
  
    // Override the configure method to specify which URLs are allowed and which are not
    protected void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests()
                .requestMatchers("/signup").permitAll() // Allow requests to /signup without authentication
                .anyRequest().authenticated() // Require authentication for all other requests
                .and()
                .formLogin() // Enable form-based login
                .loginPage("/login") // Specify the login page URL
                .failureHandler(new CustomAuthenticationFailureHandler()) // Specify the failure handler for login attempts
                .permitAll() // Allow anyone to access the login page
                .and()
                .logout() // Enable logout functionality
                .permitAll(); // Allow anyone to access the logout URL
    }
  
    // Create a PasswordEncoder bean to encode and decode passwords
    @Bean
    PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}


Step 8: Create the new package named as the request and create the java interface named as the SignRequest.

Go to src > com.gfg.customauthenticationfailure > request > SignRequest and put the below code.

Java




package com.gfg.customauthenticationfailure.request;
  
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
  
// Lombok annotations for automatically generating getters, setters, and constructors
@Data
@AllArgsConstructor
@NoArgsConstructor
public class SigninRequest {
  
    // Fields for username and password
    String username;
    String password;
}


Step 9: Create the new package named as the controller and create the java interface named as the UserController.

Go to src > com.gfg.customauthenticationfailure > controller > UserController and put the below code.

Java




package com.gfg.customauthenticationfailure.controller;
  
import com.gfg.customauthenticationfailure.model.User;
import com.gfg.customauthenticationfailure.repository.UserRepository;
import com.gfg.customauthenticationfailure.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import com.gfg.customauthenticationfailure.request.SigninRequest;
  
@RestController
public class UserController {
  
    @Autowired
    private UserRepository userRepository;
  
    @Autowired
    private PasswordEncoder passwordEncoder;
  
    // Handle the /signup endpoint for registering new users
    @PostMapping("/signup")
    public void signUp(@RequestBody User user) {
        user.setPassword(passwordEncoder.encode(user.getPassword()));
        userRepository.save(user);
    }
  
    @Autowired
    private UserService userService;
  
    // Handle the /signin endpoint for user authentication
    @PostMapping("/signin")
    public void signIn(@RequestBody SigninRequest signInRequest) throws Exception {
        // Authenticate the user manually
        User user = userService.findByUsername(signInRequest.getUsername());
        if (user != null && user.getPassword().equals(signInRequest.getPassword())) {
            // If the user is found and the password matches, authenticate the user
            Authentication authentication = new UsernamePasswordAuthenticationToken(user.getUsername(), user.getPassword());
            SecurityContextHolder.getContext().setAuthentication(authentication);
        } else {
            // If the user is not found or the password doesn't match, throw an exception
            throw new Exception("Invalid username or password");
        }
    }
}


Step 10: Open the main class and put the below code.

Java




package com.gfg.customauthenticationfailure;
  
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
  
@SpringBootApplication
public class CustomAuthenticationFailureApplication {
  
    public static void main(String[] args) {
        SpringApplication.run(CustomAuthenticationFailureApplication.class, args);  // starts the Spring Boot application
        // run the Spring Boot application
    }
    
  
}


pom.xml File:

XML




<?xml version="1.0" encoding="UTF-8"?>
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.2.3</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.gfg</groupId>
    <artifactId>CustomAuthenticationFailure</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>CustomAuthenticationFailure</name>
    <description>CustomAuthenticationFailure</description>
    <properties>
        <java.version>17</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-mongodb</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
  
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
  
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>
  
</project>


Step 11: Once completed the spring project and its run as the spring application then the application runs the port 8080.

Application Runs



Like Article
Suggest improvement
Previous
Next
Share your thoughts in the comments

Similar Reads