Spring Security Custom Authentication - AuthenticationProvider vs UserDetailsService

JavaSpringSpring MvcSpring Security

Java Problem Overview


As far as I can understand when you want custom authentication in Spring Security you can either implement a custom AuthenticationProvider or custom UserDetailsService.

@Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth	
            //.authenticationProvider(authProvider)  // option 1
            .userDetailsService(userDetailsService); // option 2

    }

In the AuthenticationProvider you can check the username and password and return Authentication with your custom object in it.

public Authentication authenticate(Authentication authentication){
        if (checkUsernameAndPassword(authentication)) {
            CustomUserDetails userDetails = new CustomUserDetails();
            //add whatever you want to the custom user details object
            return new UsernamePasswordAuthenticationToken(userDetails, password, grantedAuths);
        } else {
            throw new BadCredentialsException("Unable to auth against third party systems");
        }
    }

In the UserDetailsService you get only the username and when you return the custom UserDeatails, the framework performs a check on the password.

public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
		CustomUserDetails user = new CustomUserDetails();
		//add whatever you want to the custom user details object
		return user;
	}

Looks like both can produce similar results. So the question is what is the difference? When to user one vs the other?

Java Solutions


Solution 1 - Java

The answer is inside your question. when you are using a different authentication system, and the password is not provided in your own database/data model, you have to use the AuthenticationProvider. for example, I've worked in a project that the customer had a centralized authentication system (CAS), so my system had no idea about the password, I had to implement the AuthenticationProvider and send the given password to the CAS, and act according to its answer.

But in another system, I was storing the password in my database, so all I had to do was implementing the UserDetailsService and check if the user exists in my database or not, spring-security had to do the rest.

Solution 2 - Java

From spring security documention, https://docs.spring.io/spring-security/site/docs/5.0.0.RC1/reference/htmlsingle/#overall-architecture

>There is often some confusion about UserDetailsService. It is purely a DAO for user data and performs no other function other than to supply that data to other components within the framework. In particular, it does not authenticate the user, which is done by the AuthenticationManager. In many cases it makes more sense to implement AuthenticationProvider directly if you require a custom authentication process.

AuthenticationProvider and UserDetailsService have different purpose.

AuthenticationProvider authenticates(compares) the user(request) provided username and password against system User(This can be any system like DB which maintains list of registered users)

It is the responsiblity of UserDetailsService Implementation to get the System User Details that match with user provided Username. Here it just gets the users that have same username and does not tell the application whether authentication is successful or failed.

Example : Spring provides the following as a default setup to authenticate a user details against database

  • AuthenticationProvider - DaoAuthenticationProvider which extends AbstractUserDetailsAuthenticationProvider that calls the authenticate method by passing on username, Authentication object

  • UserDetailsService - JdbcDaoImpl

  • Flow of authentication

  1. DaoAuthenticationProvider responsibility is to authenticate the username and password obtained from request with Database user.
  2. To get the corresponding Database User, it asks UserDetailsService Implementataion JdbcDaoImpl to get a UserDetail object from database with name same as request username.Here JdbcDaoImpl just fetches the UserDetails object from system.It will either send back the user found in DB or send an exception that user is not found.
  3. If user details are found in DB, DaoAuthenticationProvider then proceeds with checking on request user password with DB found user password else fails the authentication.
  4. DaoAuthenticationProvider will respond whether the user is authenticated or not based on JdbcDaoImpl response.

Have a look here to understand it better:

AuthenticationProvider - DaoAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider

UserDetailsService - JdbcDaoImpl

UserDetails - User

Solution 3 - Java

Those two are related but is intentionally separated by Spring Security. If the enterprise has multiple systems, UserDetailsService will provide the specific user's information held by your particular system even though the authentication MIGHT be carried out by totally another system. In a simple system, they can be combined. For example, a database call will verify the username/password and retrieve all that user's email, ID and etc.

According to Spring Security Reference: http://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#getting-started

There is often some confusion about UserDetailsService. It is purely a DAO for user data and performs no other function other than to supply that data to other components within the framework. In particular, it does not authenticate the user, which is done by the AuthenticationManager. In many cases it makes more sense to implement AuthenticationProvider directly if you require a custom authentication process.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionEvgeni DimitrovView Question on Stackoverflow
Solution 1 - JavaHosseinView Answer on Stackoverflow
Solution 2 - JavaGautam TadigoppulaView Answer on Stackoverflow
Solution 3 - JavaDenis WangView Answer on Stackoverflow