Open In App

Spring Security – Authentication Providers

Last Updated : 12 May, 2023
Improve
Improve
Like Article
Like
Save
Share
Report

Authentication in Spring Security refers to the process of verifying the identity of a user or a client application attempting to access a protected resource. In other words, it’s the process of validating the user’s credentials (such as username and password) to ensure that they are who they claim to be. Spring Security provides various authentication mechanisms, such as form-based authentication, HTTP Basic authentication, and OAuth, among others. The authentication process typically involves collecting the user’s credentials through a login form, validating them against a user database or authentication provider, and creating a security context that represents the authenticated user.

Importance of Authentication Providers in Spring Security

Authentication providers are an essential part of Spring Security as they play a critical role in verifying user credentials during the authentication process. Here are some of the key reasons why we need authentication providers:

  • Verify user identity: Authentication providers are responsible for verifying the user’s identity by checking their credentials, such as a username and password, against a known database or identity provider.
  • Ensure security: Authentication providers help ensure the security of an application by preventing unauthorized access to sensitive resources. They do this by verifying that the user is who they claim to be and has the necessary permissions to access the requested resource.
  • Support for multiple authentication mechanisms: Authentication providers can support multiple authentication mechanisms, such as username/password, biometrics, or multifactor authentication, to provide increased security and flexibility.
  • Integration with external identity providers: Authentication providers can be integrated with external identity providers, such as LDAP, OAuth, or SAML, to provide a seamless and secure login experience for users.

Authentication Providers

Explanation of AuthenticationProvider interface:

  • The  AuthenticationProvider interface is a key component of Spring Security’s authentication and authorization framework. It is responsible for authenticating a user’s credentials and returning an Authentication object that represents the authenticated user. Here is an explanation of the key methods and responsibilities of the AuthenticationProvider interface:
    • Authentication authenticate(Authentication authentication): This method takes an Authentication object as an argument and returns an Authentication object if the authentication is successful, otherwise, it throws an AuthenticationException. The Authentication object represents the authenticated identity of the user.
    • boolean supports(Class<?> authentication): This method is used to check whether the AuthenticationProvider implementation supports the type of Authentication object that is passed to the authenticate() method. It returns true if the provider supports the authentication request, and false otherwise.
  • The AuthenticationProvider interface is designed to support a wide range of authentication scenarios. For example, an implementation of this interface can perform the following actions:
    • Check the user’s credentials against a database or external identity providers, such as LDAP or OAuth.
    • Perform additional validation of the user’s credentials, such as checking for password expiration or account lockout.
    • Assign user roles and permissions based on the user’s credentials.
    • Return additional information about the authenticated user, such as their full name or email address.
  • Most common authentication providers in Spring Security:
    1. DaoAuthenticationProvider
    2. LdapAuthenticationProvider
    3. OpenIDAuthenticationProvider
    4. JwtAuthenticationProvider
    5. RememberMeAuthenticationProvider

1. DaoAuthenticationProvider

  • DaoAuthenticationProvider is an authentication provider in Spring Security that is used to authenticate users stored in a database. It implements the AuthenticationProvider interface and can be used with any database that provides a JDBC driver.
  • DaoAuthenticationProvider retrieves the user’s credentials, such as username and password, from the database and compares them to the credentials provided by the user during login. If the credentials match, the provider creates an Authentication object representing the authenticated user.
  • To use DaoAuthenticationProvider, you must provide a UserDetailsService implementation that retrieves user information from the database. UserDetailsService provides the username, password, and authorities for a given user. DaoAuthenticationProvider uses UserDetailsService to retrieve user information from the database during authentication.
  • To use the DaoAuthenticationProvider, you need to configure it in your Spring Security configuration file. 

Example:

Here’s an example of how to configure the DaoAuthenticationProvider with an in-memory UserDetailsService and a BCryptPasswordEncoder:

1. First, you need to configure a DataSource bean to provide the database connection

Java




@Bean 
public DataSource dataSource()
{
    DriverManagerDataSource dataSource
        = new DriverManagerDataSource();
    dataSource.setDriverClassName("org.postgresql.Driver");
    dataSource.setUrl(
        "jdbc:postgresql://localhost:5432/mydatabase");
    dataSource.setUsername("myuser");
    dataSource.setPassword("mypassword");
    return dataSource;
}


2. Next, you need to create a UserDetailsServicebean that will retrieve user information from the database. Here’s an example implementation

Java




@Service
public class MyUserDetailsService implements UserDetailsService {
  
    private final UserRepository userRepository;
  
    public MyUserDetailsService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }
  
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userRepository.findByUsername(username);
        if (user == null) {
            throw new UsernameNotFoundException("User not found");
        }
        return new org.springframework.security.core.userdetails.User(
            user.getUsername(),
            user.getPassword(),
            user.getRoles().stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList())
        );
    }
  
}


3. Finally, you can configure DaoAuthenticationProvider in your SecurityConfig class

Java




@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
  
    private final MyUserDetailsService userDetailsService;
    private final PasswordEncoder passwordEncoder;
  
    public SecurityConfig(MyUserDetailsService userDetailsService, PasswordEncoder passwordEncoder) {
        this.userDetailsService = userDetailsService;
        this.passwordEncoder = passwordEncoder;
    }
  
    @Bean
    public DaoAuthenticationProvider authenticationProvider() {
        DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
        provider.setUserDetailsService(userDetailsService);
        provider.setPasswordEncoder(passwordEncoder);
        return provider;
    }
  
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(authenticationProvider());
    }
  
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .antMatchers("/admin/**").hasRole("ADMIN")
                .antMatchers("/user/**").hasRole("USER")
                .anyRequest().authenticated()
                .and()
            .formLogin()
                .loginPage("/login")
                .permitAll()
                .and()
            .logout()
                .permitAll();
    }
  
}


2. LdapAuthenticationProvider

  • LdapAuthenticationProvider is a specific implementation of AuthenticationProvider that authenticates users against an LDAP (Lightweight Directory Access Protocol) server.
  • LDAP is a protocol used for accessing and maintaining distributed directory information services over an Internet Protocol (IP) network. It is commonly used for managing user authentication and authorization information, such as usernames, passwords, and roles.
  • The LdapAuthenticationProvider class in Spring Security provides an implementation of the AuthenticationProvider interface that delegates authentication to an LDAP server. It takes a LdapAuthenticator instance, which is responsible for performing the actual authentication against the LDAP server.
  • To configure a LdapAuthenticationProviderin Spring Security, you need to provide the LdapAuthenticator and LdapAuthoritiesPopulatorobjects, as well as other optional settings such as the LDAP server URL, bind credentials, and search base.

Example: 

Here’s an example of how to configure LdapAuthenticationProvider in Spring Security.

1. Define the LdapContextSource bean to configure the connection to the LDAP server

Java




@Bean
public LdapContextSource contextSource() {
    LdapContextSource contextSource = new LdapContextSource();
    contextSource.setUrl("ldap://localhost:389");
    contextSource.setBase("dc=example,dc=com");
    contextSource.setUserDn("cn=admin,dc=example,dc=com");
    contextSource.setPassword("password");
    return contextSource;
}


2. Define the LdapUserSearch bean to configure how to search for a user in the LDAP server

Java




@Bean
public LdapUserSearch ldapUserSearch() {
    return new FilterBasedLdapUserSearch("ou=users,dc=example,dc=com", "(uid={0})", contextSource());
}


3. Define the LdapAuthoritiesPopulator bean to configure how to retrieve the authorities for a user from the LDAP server

Java




@Bean
public LdapAuthoritiesPopulator authoritiesPopulator() {
    return new DefaultLdapAuthoritiesPopulator(contextSource(), "ou=groups");
}


4. Define the LdapAuthenticationProvider bean that wraps all the previous beans and provides the authentication functionality:

Java




@Bean
public LdapAuthenticationProvider authenticationProvider() {
    return new LdapAuthenticationProvider(ldapAuthenticator(), authoritiesPopulator());
}


5. Configure the AuthenticationManagerBuilder to use the LdapAuthenticationProvider bean

Java




@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
    auth.authenticationProvider(authenticationProvider());
}


3. OpenIDAuthenticationProvider

  • OpenIDAuthenticationProvider is a class in Spring Security that provides support for OpenID authentication. OpenID is an open standard for authentication that enables users to authenticate themselves to a website or application using an OpenID provider, such as Google or Facebook.
  • In Spring Security, the OpenIDAuthenticationProvider is responsible for authenticating users based on OpenID authentication. It takes an OpenID4JavaConsumer instance, which is responsible for performing the actual authentication against the OpenID provider.
  • To use OpenIDAuthenticationProvider, you need to configure an OpenID consumer and provider. The OpenID4JavaConsumer class handles the OpenID authentication flow and retrieves the user’s information from the OpenID provider. 
  • You need to provide the OpenID4JavaConsumer with the following information:
    • The OpenID provider URL: This is the URL of the OpenID provider that will be used to authenticate the user.
    • The return URL: This is the URL that the OpenID provider will return the authentication response to after the user has been authenticated.
    • The realm: This is a unique identifier for the relying party (your application) that is requesting authentication from the OpenID provider.

Example:

Here’s an example of how to configure OpenIDAuthenticationProvider in Spring Security

1. First, configure an OpenID4JavaConsumer bean to handle the OpenID authentication flow and retrieve user information from the OpenID provider.

Java




@Bean
public OpenID4JavaConsumer openID4JavaConsumer() {
    OpenID4JavaConsumer consumer = new OpenID4JavaConsumer();
    consumer.setReturnToUrl(env.getProperty("security.openid.returnToUrl"));
    consumer.setRealm(env.getProperty("security.openid.realm"));
    consumer.setProviderUrl(env.getProperty("security.openid.providerUrl"));
    return consumer;
}


2. Next, create an OpenIDAuthenticationProvider bean and configure it with the OpenID4JavaConsumer bean.

Java




@Bean
public OpenIDAuthenticationProvider openIDAuthenticationProvider() {
    OpenIDAuthenticationProvider provider = new OpenIDAuthenticationProvider();
    provider.setConsumer(openID4JavaConsumer());
    return provider;
}


3. Add the OpenIDAuthenticationProvider to the AuthenticationManagerBuilder to handle OpenID authentication requests.

Java




@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.authenticationProvider(openIDAuthenticationProvider());
}


4. Finally, configure the OpenIDAuthenticationFilter to intercept and handle OpenID authentication requests.

Java




@Override
protected void configure(HttpSecurity http) throws Exception {
    http
        // Other security configuration...
        .addFilterAfter(openIDAuthenticationFilter(), AbstractPreAuthenticatedProcessingFilter.class)
        // Other security configuration...
}
  
@Bean
public OpenIDAuthenticationFilter openIDAuthenticationFilter() {
    OpenIDAuthenticationFilter filter = new OpenIDAuthenticationFilter();
    filter.setConsumer(openID4JavaConsumer());
    filter.setAuthenticationManager(authenticationManager());
    filter.setAuthenticationSuccessHandler(openIDAuthenticationSuccessHandler());
    filter.setAuthenticationFailureHandler(openIDAuthenticationFailureHandler());
    return filter;
}
  
@Bean
public AuthenticationSuccessHandler openIDAuthenticationSuccessHandler() {
    // Define a success handler to handle successful OpenID authentication
}
  
@Bean
public AuthenticationFailureHandler openIDAuthenticationFailureHandler() {
    // Define a failure handler to handle failed OpenID authentication
}


4. JwtAuthenticationProvider

  • JwtAuthenticationProvider is an implementation of the Spring Security AuthenticationProvider interface that is used to authenticate users based on JSON Web Tokens (JWTs).
  • When a JWT is presented for authentication, the JwtAuthenticationProvider verifies the token’s signature and extracts the user’s identity information from the token. It then creates a JwtAuthenticationToken object, which encapsulates the authenticated user’s details.
  • To use JwtAuthenticationProvider in Spring Security, you will need to configure it with a JwtDecoder instance that will be used to decode the JWT. The JwtDecoder is responsible for verifying the signature and parsing the JWT payload.

Example:

Here’s an example of how to configure JwtAuthenticationProvider in Spring Security:

1. First, configure a JwtDecoder bean that will be used to decode the JWT and verify its signature.

Java




@Bean
public JwtDecoder jwtDecoder() {
    String secret = "mySecretKey";
    return NimbusJwtDecoder.withSecretKey(new SecretKeySpec(secret.getBytes(), SignatureAlgorithm.HS256.getJcaName()))
            .build();
}


2. Next, create a JwtAuthenticationProvider bean and configure it with the JwtDecoder bean.

Java




@Bean
public JwtAuthenticationProvider jwtAuthenticationProvider() {
    return new JwtAuthenticationProvider(jwtDecoder());
}


3. Add the JwtAuthenticationProvider to the AuthenticationManagerBuilder to handle JWT authentication requests.

Java




@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.authenticationProvider(jwtAuthenticationProvider());
}


4. Finally, configure the JwtAuthenticationFilter to intercept and handle JWT authentication requests.

Java




@Override
protected void configure(HttpSecurity http) throws Exception {
    http
        // Other security configuration...
        .addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
        // Other security configuration...
}
  
@Bean
public JwtAuthenticationFilter jwtAuthenticationFilter() {
    JwtAuthenticationFilter filter = new JwtAuthenticationFilter();
    filter.setAuthenticationManager(authenticationManager());
    filter.setAuthenticationSuccessHandler(jwtAuthenticationSuccessHandler());
    filter.setAuthenticationFailureHandler(jwtAuthenticationFailureHandler());
    return filter;
}
  
@Bean
public AuthenticationSuccessHandler jwtAuthenticationSuccessHandler() {
    // Define a success handler to handle successful JWT authentication
}
  
@Bean
public AuthenticationFailureHandler jwtAuthenticationFailureHandler() {
    // Define a failure handler to handle failed JWT authentication
}


5. RememberMeAuthenticationProvider

  • RememberMeAuthenticationProvider is an implementation of the Spring Security AuthenticationProvider interface that is used to authenticate users based on previously saved remember-me tokens.
  • When a user logs in with the “remember me” option selected, Spring Security will issue a remember-me token and store it in a persistent storage, such as a database or a cookie. The next time the user visits the site, Spring Security will attempt to authenticate the user using the remember-me token. This is where RememberMeAuthenticationProvider comes into play.
  • RememberMeAuthenticationProvider is responsible for validating the remember-me token and creating an Authentication object based on the token. If the token is valid, RememberMeAuthenticationProvider will create a new RememberMeAuthenticationToken and return it. If the token is not valid, RememberMeAuthenticationProvider will throw a BadCredentialsException.
  • To use RememberMeAuthenticationProvider in Spring Security, you will need to configure it with a UserDetailsService that will be used to load the user’s details based on the remember-me token.

Example:

Here’s an example of how to configure RememberMeAuthenticationProvider in Spring Security:

1. Define a UserDetailsService implementation that retrieves user details from your application’s database:

Java




@Service
public class UserDetailsServiceImpl implements UserDetailsService {
      
    @Autowired
    private UserRepository userRepository;
  
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userRepository.findByUsername(username);
        if (user == null) {
            throw new UsernameNotFoundException("User not found with username: " + username);
        }
        return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(),
                Collections.singletonList(new SimpleGrantedAuthority(user.getRole().name())));
    }
}


2. Configure authentication using the UserDetailsService implementation:

Java




@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
  
    @Autowired
    private UserDetailsService userDetailsService;
  
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService);
    }
  
    // ...
}


3. Define a RememberMeAuthenticationProvider bean:

Java




@Bean
public RememberMeAuthenticationProvider rememberMeAuthenticationProvider() {
    return new RememberMeAuthenticationProvider("myAppKey");
}


4. Define a PersistentTokenRepository implementation that stores remember-me tokens in your application’s database:

Java




@Bean
public PersistentTokenRepository persistentTokenRepository() {
    JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl();
    tokenRepository.setDataSource(dataSource); // configure DataSource
    return tokenRepository;
}


5. Define a TokenBasedRememberMeServices bean that uses the RememberMeAuthenticationProvider and PersistentTokenRepository:

Java




@Bean
public TokenBasedRememberMeServices rememberMeServices() {
    TokenBasedRememberMeServices rememberMeServices = new TokenBasedRememberMeServices("myAppKey", userDetailsService,
            persistentTokenRepository());
    rememberMeServices.setTokenValiditySeconds(604800); // one week
    return rememberMeServices;
}


6. Configure remember-me authentication in your HttpSecurity configuration:

Java




@Override
protected void configure(HttpSecurity http) throws Exception {
    http
        .authorizeRequests()
            .antMatchers("/public/**").permitAll()
            .anyRequest().authenticated()
            .and()
        .formLogin()
            .loginPage("/login")
            .defaultSuccessUrl("/dashboard")
            .and()
        .rememberMe()
            .key("myAppKey")
            .rememberMeServices(rememberMeServices())
            .and()
        .logout()
            .logoutUrl("/logout")
            .logoutSuccessUrl("/public")
            .and()
        .exceptionHandling()
            .accessDeniedPage("/403")
            .and()
        .csrf()
            .disable();
}


Custom Authentication Provider

Explanation of how to create custom authentication and authorization providers:

  • To create a custom authentication provider in Spring Security, you need to implement the AuthenticationProvider interface. The AuthenticationProvider interface has a single method authenticate(Authentication authentication) which takes an Authentication object as input and returns an Authentication object as output. The authenticate() method is responsible for authenticating the user based on the provided credentials and returning an Authentication object with the user’s details.
  • Here’s a step-by-step guide on how to create a custom authentication provider in Spring Security:

1. Create a new class that implements the AuthenticationProvider interface.

Java




public class CustomAuthenticationProvider implements AuthenticationProvider {
      
    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        // TODO: Implement authentication logic
    }
  
    @Override
    public boolean supports(Class<?> authentication) {
        return authentication.equals(UsernamePasswordAuthenticationToken.class);
    }
}


2. Implement the authenticate(Authentication authentication) method. This method takes an Authentication object as input and returns an Authentication object as output.

Java




public class CustomAuthenticationProvider implements AuthenticationProvider {
      
    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        String username = authentication.getName();
        String password = authentication.getCredentials().toString();
  
        // TODO: Implement authentication logic
  
        List<GrantedAuthority> authorities = new ArrayList<>();
        authorities.add(new SimpleGrantedAuthority("ROLE_USER"));
        return new UsernamePasswordAuthenticationToken(username, password, authorities);
    }
  
    @Override
    public boolean supports(Class<?> authentication) {
        return authentication.equals(UsernamePasswordAuthenticationToken.class);
    }
}


3. In the authenticate() method, implement the logic to authenticate the user based on the provided credentials. This can be done by querying a database or an external authentication service.

Java




public class CustomAuthenticationProvider implements AuthenticationProvider {
      
    @Autowired
    private UserService userService;
  
    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        String username = authentication.getName();
        String password = authentication.getCredentials().toString();
  
        User user = userService.findByUsername(username);
  
        if (user == null || !password.equals(user.getPassword())) {
            throw new BadCredentialsException("Invalid username or password");
        }
  
        List<GrantedAuthority> authorities = new ArrayList<>();
        authorities.add(new SimpleGrantedAuthority("ROLE_USER"));
        return new UsernamePasswordAuthenticationToken(username, password, authorities);
    }
  
    @Override
    public boolean supports(Class<?> authentication) {
        return authentication.equals(UsernamePasswordAuthenticationToken.class);
    }
}


4. Implement the supports(Class<?> authentication) method. This method returns true if the authentication provider supports the specified authentication token class. In this example, we’re only supporting the UsernamePasswordAuthenticationToken class.

Java




public class CustomAuthenticationProvider implements AuthenticationProvider {
      
    @Autowired
    private UserService userService;
  
    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        // TODO: Implement authentication logic
    }
  
    @Override
    public boolean supports(Class<?> authentication) {
        return authentication.equals(UsernamePasswordAuthenticationToken.class);
    }
}


5. Inject the custom authentication provider into your AuthenticationManagerBuilder in your Spring Security configuration.

Java




@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
  
    @Autowired
    private CustomAuthenticationProvider customAuthenticationProvider;
  
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(customAuthenticationProvider);
    }
  
    // ...
}


Conclusion

  • Spring Security provides a robust authentication and authorization framework for securing web applications. It includes a wide range of authentication providers, each with its own strengths and weaknesses, that can be used to authenticate users and grant them access to protected resources.
  • The choice of authentication provider depends on the specific requirements of the application. For example, if the application needs to authenticate users against an LDAP directory, the LDAP authentication provider can be used. If the application requires social login functionality, the OAuth2 authentication provider can be used.
  • In general, Spring Security provides a comprehensive set of authentication providers that can be used to secure different types of applications.


Like Article
Suggest improvement
Share your thoughts in the comments

Similar Reads