Spring Security: Authentication

Authentication is the process of verifying the identity of a user or system. It's the first step in a security process that ensures that users are who they claim to be. Spring Security provides a comprehensive authentication model, supporting many authentication methods such as basic authentication, form-based authentication, JWT token-based authentication, and OAuth2.

The Authentication Process in Spring Security

User Credentials Submission: The user submits credentials, usually via a login form. 

Authentication Token Creation: Spring Security creates an Authentication token representing the user's credentials. 

Authentication Manager: The AuthenticationManager authenticates the Authentication token using an AuthenticationProvider

Security Context: Upon successful authentication, the Authentication token is stored in the SecurityContextHolder, making the user's details globally accessible.

Username/Password Authentication (In-memory Authentication)

One of the most common ways to authenticate a user is by validating a username and password. Spring Security provides comprehensive support for authenticating with a username and password. You can configure username and password authentication using the following:
@Configuration
@EnableWebSecurity
public class SecurityConfig {

	@Bean
	public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
		http
			.authorizeHttpRequests((authorize) -> authorize
				.anyRequest().authenticated()
			)
			.httpBasic(Customizer.withDefaults())
			.formLogin(Customizer.withDefaults());

		return http.build();
	}

	@Bean
	public UserDetailsService userDetailsService() {
		UserDetails userDetails = User.withDefaultPasswordEncoder()
			.username("user")
			.password("password")
			.roles("USER")
			.build();

		return new InMemoryUserDetailsManager(userDetails);
	}

}
The preceding configuration automatically registers an in-memory UserDetailsService with the SecurityFilterChain, registers the DaoAuthenticationProvider with the default AuthenticationManager, and enables Form Login and HTTP Basic authentication. 

Custom Authentication with UserDetailsService (Database Authentication)

To load user details from a custom source (e.g., a database), you can define a UserDetailsService bean. This service is used by Spring Security during the authentication process to load user-specific data. 

For example, Here is a logic to load user details by name or email from the database:
@Service
@AllArgsConstructor
public class CustomUserDetailsService implements UserDetailsService {

    private UserRepository userRepository;

    @Override
    public UserDetails loadUserByUsername(String usernameOrEmail) throws UsernameNotFoundException {

        User user = userRepository.findByUsernameOrEmail(usernameOrEmail, usernameOrEmail)
                .orElseThrow(() -> new UsernameNotFoundException("User not exists by Username or Email"));

        Set<GrantedAuthority> authorities = user.getRoles().stream()
                .map((role) -> new SimpleGrantedAuthority(role.getName()))
                .collect(Collectors.toSet());

        return new org.springframework.security.core.userdetails.User(
                usernameOrEmail,
                user.getPassword(),
                authorities
        );
    }
}

Here, the CustomUserDetailsService implements the UserDetailsService interface ( Spring security in-build interface) and provides an implementation for the loadUserByUername() method. Next, Spring Security uses the UserDetailsService interface, which contains the loadUserByUsername(String username) method to look up UserDetails for a given username.

Different Authentication Methods in Spring Security 

Form-Based Authentication 

The simplest and most common authentication type is form-based authentication, where users provide their username and password through a form.
@Configuration
@EnableWebSecurity
public class SecurityConfig {

	@Bean
	public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
		http
			.authorizeHttpRequests((authorize) -> authorize
				.anyRequest().authenticated()
			)
			.formLogin(Customizer.withDefaults());

		return http.build();
	}
}
This configuration sets up form-based authentication for all requests, directing unauthenticated users to a default login page provided by Spring Security. 

Basic Authentication 

Basic Authentication is a simple authentication scheme built into the HTTP protocol. It's commonly used for API authentication. 

By default, Spring Security’s HTTP Basic Authentication support is enabled. However, as soon as any servlet-based configuration is provided, HTTP Basic must be explicitly provided.
@Configuration
@EnableWebSecurity
public class SecurityConfig {

	@Bean
	public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
		http
			.authorizeHttpRequests((authorize) -> authorize
				.anyRequest().authenticated()
			)
			.httpBasic(Customizer.withDefaults())

		return http.build();
	}

}

JWT Authentication 

JSON Web Tokens (JWT) offer a more sophisticated method for handling authentication and authorization, especially in RESTful APIs and microservices. 

Integrating JWT with Spring Security requires more setup than form-based or basic authentication. You typically need to implement a filter to parse and validate the JWT from the Authorization header of incoming requests. 

Here is a JwtAuthenticationFilter class in a Spring Boot application that intercepts incoming HTTP requests and validates JWT tokens that are included in the Authorization header. If the token is valid, the filter sets the current user's authentication in the SecurityContext.
@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {

    private JwtTokenProvider jwtTokenProvider;

    private UserDetailsService userDetailsService;

    public JwtAuthenticationFilter(JwtTokenProvider jwtTokenProvider, UserDetailsService userDetailsService) {
        this.jwtTokenProvider = jwtTokenProvider;
        this.userDetailsService = userDetailsService;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain filterChain) throws ServletException, IOException {

        // Get JWT token from HTTP request
        String token = getTokenFromRequest(request);

        // Validate Token
        if(StringUtils.hasText(token) && jwtTokenProvider.validateToken(token)){
            // get username from token
            String username = jwtTokenProvider.getUsername(token);

            UserDetails userDetails = userDetailsService.loadUserByUsername(username);

            UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(
              userDetails,
              null,
              userDetails.getAuthorities()
            );

            authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));

            SecurityContextHolder.getContext().setAuthentication(authenticationToken);
        }

        filterChain.doFilter(request, response);
    }

    private String getTokenFromRequest(HttpServletRequest request){
        String bearerToken = request.getHeader("Authorization");

        if(StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")){
            return bearerToken.substring(7, bearerToken.length());
        }

        return null;
    }
}

OAuth2 

OAuth2 is an authorization framework that allows third-party services to exchange web resources on behalf of a user. Spring Security provides comprehensive support for OAuth2, enabling scenarios such as login with external providers (e.g., Google, Facebook). Configuring OAuth2 with Spring Security is streamlined with Spring Boot's OAuth2 client support.
@Configuration
@EnableWebSecurity
public class SecurityConfig {

	@Bean
	public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
		http
			// ...
			.oauth2Login(Customizer.withDefaults());
		return http.build();
	}

}

Conclusion 

Spring Security's authentication mechanisms provide a powerful and flexible way to secure your application. Whether you're securing a web application with form-based login, protecting API endpoints with Basic or JWT authentication, or integrating with external authentication providers through OAuth2, Spring Security has you covered. By understanding and applying these authentication mechanisms, you can ensure that your application remains secure, providing peace of mind in an increasingly insecure digital landscape.

Comments