What is an Authentication Provider in Spring Security?

Authentication Provider in Spring Security plays a crucial role in the authentication process. It is responsible for verifying the credentials provided by a user, such as a username and password, against a known source of user information, like a database or an LDAP server. If the authentication is successful, it returns an Authentication object representing the authenticated user.

How Does an Authentication Provider Work?

The internal workings of an AuthenticationProvider in Spring Security can be summarized in the following steps:

  1. Receive Authentication Request: The AuthenticationProvider receives an Authentication object containing the user’s credentials.
  2. Validate Credentials: It verifies the provided credentials against a user data source (e.g., a database).
  3. Create Authentication Object: If the credentials are valid, it creates an Authentication object with the user’s details and granted authorities (roles/permissions).
  4. Return Authentication Object: The AuthenticationProvider returns the Authentication object. If authentication fails, it throws an AuthenticationException.

The AuthenticationProvider interface has two main methods:

Java
public interface AuthenticationProvider {
    Authentication authenticate(Authentication authentication) throws AuthenticationException;
    boolean supports(Class<?> authentication);
}
  • authenticate: This method takes an Authentication object and returns a fully authenticated object if the credentials are valid.
  • supports: This method indicates whether the AuthenticationProvider can handle the type of Authentication object passed to it.

Creating a Custom Authentication Provider

Creating a custom AuthenticationProvider involves implementing the AuthenticationProvider interface and defining how the authentication process should work.

Step 1: Implement the Custom Authentication Provider

Java
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;

public class CustomAuthProvider implements AuthenticationProvider {

    private final UserDetailsService userDetailsService;
    private final PasswordEncoder passwordEncoder;

    @Autowired
    public CustomAuthProvider(UserDetailsService userDetailsService, PasswordEncoder passwordEncoder) {
        this.userDetailsService = userDetailsService;
        this.passwordEncoder = passwordEncoder;
    }

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        String username = authentication.getName();
        String password = authentication.getCredentials().toString();

        UserDetails user = userDetailsService.loadUserByUsername(username);
        if (user == null) {
            throw new BadCredentialsException("User not found");
        }

        if (!passwordEncoder.matches(password, user.getPassword())) {
            throw new BadCredentialsException("Invalid password");
        }

        return new UsernamePasswordAuthenticationToken(user, password, user.getAuthorities());
    }

    @Override
    public boolean supports(Class<?> authentication) {
        return UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication);
    }
}

Step 2: Configure Spring Security to Use the Custom Authentication Provider

Java
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.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    private final CustomAuthProvider customAuthProvider;

    @Autowired
    public SecurityConfig(CustomAuthProvider customAuthProvider) {
        this.customAuthProvider = customAuthProvider;
    }

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests((requests) -> requests
                .requestMatchers("/login", "/logout", "/public/**").permitAll()
                .anyRequest().authenticated()
            )
            .formLogin((form) -> form
                .loginPage("/login")
                .permitAll()
            )
            .logout((logout) -> logout
                .logoutUrl("/logout")
                .logoutSuccessUrl("/login?logout")
                .invalidateHttpSession(true)
                .deleteCookies("JSESSIONID")
            )
            .authenticationProvider(customAuthProvider);

        return http.build();
    }

    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
        return authenticationConfiguration.getAuthenticationManager();
    }

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

Alternatively, use can use XML Configuration

If you prefer XML configuration over Java configuration, you can define the custom AuthenticationProvider as follows:

Step 1: Define the Custom Authentication Provider Bean

XML
<beans:bean id="customAuthProvider" class="com.example.security.CustomAuthProvider">
    <beans:constructor-arg ref="userDetailsService" />
    <beans:constructor-arg ref="passwordEncoder" />
</beans:bean>

Step 2: Configure Spring Security to Use the Custom Authentication Provider

XML
<security:http>
    <security:intercept-url pattern="/login" access="permitAll" />
    <security:intercept-url pattern="/logout" access="permitAll" />
    <security:intercept-url pattern="/public/**" access="permitAll" />
    <security:intercept-url pattern="/**" access="authenticated" />

    <security:form-login login-page="/login" />
    <security:logout logout-url="/logout" logout-success-url="/login?logout"
                     invalidate-session="true" delete-cookies="JSESSIONID" />

    <security:authentication-manager>
        <security:authentication-provider ref="customAuthProvider" />
    </security:authentication-manager>
</security:http>

Step 3: Define Supporting Beans (UserDetailsService and PasswordEncoder)

XML
<beans:bean id="userDetailsService" class="com.example.security.CustomUserDetailsService" />
<beans:bean id="passwordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder" />

Conclusion:

In Spring Security 6.2.x, the AuthenticationProvider plays a vital role in the authentication process by verifying user credentials against a known data source. By implementing a custom AuthenticationProvider in Spring security, you can tailor the authentication logic to meet your specific requirements. Whether using Java or XML configuration, integrating a custom provider into your Spring Security setup enhances the flexibility and security of your authentication process. This guide has provided a comprehensive overview of how to create and configure a custom AuthenticationProvider, enabling you to build more secure and user-friendly applications.

By |Last Updated: May 23rd, 2024|Categories: Spring Security|

Table of Contents