Spring Security in detail
Initial step:
Steps to configure web MVC by adding dependencies explicitly without Spring Boot auto-configure -
- Add dependencies
- Extend AbstractAnnotationConfigDispatcherServletInitializer, then add implementation for all the methods -
- Provide a configuration class for the getServletConfigClasses method with the custom config class.
- Provide URL mapping for the getServletMapping method.
- In the custom config class should be marked with @Configuration, @EnableWebMvc and @ComponentScan annotations. Also, this class should create a bean for the view resolver if the view page exists in the project folder location. (create InternalResourceViewresolver bean).
Adding Spring Security -
Before the dispatcher dispatches the request Spring security filter comes in the action to filter the requests. It is just a simple servlet filter.
- Add dependencies
-
Create a custom web security class for your application by extending WebSecurityConfigurerAdapter and mark this class with @EnableWebSecurity, then this custom web security should be attached with Spring security using custom security initializer.
-
To create custom security initializer extend AbstractSecurityWebApplicationInitializer
-
With respect to the custom web security class in the step 2 inherit method configure(HttpSecurity http) which contains default behaviour to authenticate form based and rest client. This can be overridden by the custom authentication mechanism. This method is used to configure all the endpoints and authorization.
- Override configure(AuthenticationManagerBuilder auth) to authenticate the user based upon requirement, here we can configure different types of authentication such as in-memory/jdbc/ldap etc.
Note -
Spring Security basic architecture
-
UserDetails is an interface that holds the user’s details. e.g Name, Id, GrantedAuthority, Account Expiry, Account Locked, Password, etc.
-
UserDetailsinterface is implemented in the Users class provided by Spring also it implements another interface called CredentialContainer. -
UserDetailstype is used in the different implementations of UserDetailsManager which wraps the user’s info. -
There are different types of
UserDetailsManagerimplementation such as InMemoryUserDetailsManager, and JdbcUserDetailsManager and it can be implemented to define a custom user details manager. -
The
UserDetailsManagerinterface extends another interface known as UserDetailsService, this has only one method loadUserByUserName(String username). The same method will be inherited by all the child classes. -
Having
loadUserByUserName(String username)separately in theUserDetailsServiceinterface makes it easier to create a custom user details manager. -
loadUserByUserName(String username)is called by different authentication provider. One of the authentication providers is DaoAuthenticationProvider which extends AbstractUserDetailsAuthenticationProvider. -
AbstractUserDetailsAuthenticationProviderclass implements AuthenticationProvider interface which has authenticate(Authentication authentication) method. -
AbstractUserDetailsAuthenticationProviderprovides implementation of authenticate(Authentication authentication) method, which makes calls to the abstract method retrieveUser(String username, UsernamePassowrdAuthenticationToken authentication) and this is implemented in one of the concrete classes known as DaoAuthenticationProvider.

-
Custom authentication provider can be introduced by implementing the AuthenticationProvider interface.
-
AuthenticationManager interface scan all the authentication provider to authenticate.
-
AuthenticationManagerhas a couple of implementations; one of the concrete implementations is ProviderManager. -
ProviderManager class implements the authenticate(Authentication authentication) method calling the
AuthenticationProviderin the runtime. -
AuthenticationManagerimplementation is called by AuthenticationFilter class which creates anAuthenticationobject from the HTTP request. -
Authentication is an interface that extends the Principal : Java 17 interface.

-
Default and form-based authentication create UsernamePasswordAuthenticationToken object which is of the
Authenticationtype. -
There are different types of Spring-provided filters, one of the filters is UsernamePasswordAuthenticationFilter which makes requests to the implementation of the
AuthenticationManagerinterface for all the configuredAuthenticationProviderimplementations. - Filters in the spring can be configured in different ways, here are two simple approaches -
-
Extending the existing Spring provided filter and override the
doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)E.g OncePerRequestFilterpublic class CustomFilter extends OncePerRequestFilter { @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { /** * Custom logic can be here. */ filterChain.doFilter(request, response); } } -
Implementing
javax.servlet.Filterand override thedoFilter(ServletRequest request, ServletResponse response, FilterChain chain)method, but it needs to be configured in the custom SecurityConfig class.public class CustomFilter implements Filter { @Override protected void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { /** * Custom logic can be here. */ chain.doFilter(request, response); } } @EnableWebSecurity public class SecurityConfig { @Bean protected SecurityFilterChain filterChain(HttpSecurity http) throws Exception { return http .addFilterAfter(new CustomFilter(), UsernamePasswordAuthenticationFilter.class) .requestMatchers().antMatchers("/home").and().csrf().disable() .authorizeRequests().anyRequest().permitAll() .and().build(); } }
-
- The default behavior of
UsernamePasswordAuthenticationFilterfor the form-based login, this class extends AbstractAuthenticationProcessingFilter that extends GenericFilterBean andGenericFilterBeanimplementsjavax.servlet.Filter.UsernamePasswordAuthenticationFilteroverridesattemptAuthentication(HttpServletRequest request, HttpServletResponse response)and it creates default UsernamePasswordAuthenticationToken object that is indirectly Authentication type. Below is the reference

-
AuthenticationProviderin the spring can be configured in different ways. Following are the steps to configure a custom authentication provider :-
Create a custom filter to register a custom authentication type. The filter should extends
AbstractAuthenticationProcessingFilterand overrideattemptAuthentication(HttpServletRequest request, HttpServletResponse response)method then return the custom type authentication object and this custom type authentication object should be of typeorg.springframework.security.core.Authentication.public class CustomAuthenticationType implements org.springframework.security.core.Authentication { }public class CustomFilter extends AbstractAuthenticationProcessingFilter { protected CustomFilter(RequestMatcher requiresAuthenticationRequestMatcher) { super(requiresAuthenticationRequestMatcher); } @Override public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException { /** * Custom logic can be here. */ CustomAuthenticationType customAuthenticationType = new CustomAuthenticationType(); return this.getAuthenticationManager().authenticate(customAuthenticationType); } } - Create a custom authentication provider by implementing AuthenticationProvider. Among two of the overridden method the
supports(Class<?> authentication)method let spring know whether to execute a custom authentication provider or not.@Component public class CustomAuthenticationProviderImpl implements AuthenticationProvider { @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { /** * This is just a sample code and will have some core logic */ return new CustomAuthenticationType(); } @Override public boolean supports(Class<?> authentication) { return authentication.equals(CustomAuthenticationType.class); } } - Add custom authentication provider in the custom SecurityConfig class.
@EnableWebSecurity public class SecurityConfig { @Autowired private CustomAuthenticationProviderImpl customAuthenticationProviderImpl; @Bean protected SecurityFilterChain filterChain(HttpSecurity http) throws Exception { return http .authenticationProvider(customAuthenticationProviderImpl) .addFilterAfter(new CustomFilter(), UsernamePasswordAuthenticationFilter.class) .requestMatchers().antMatchers("/home").and().csrf().disable() .authorizeRequests().anyRequest().permitAll() .and().build(); } } -
Spring
ProviderManagerscan thru all the registered authentication provider and then execute that authentication mechanism. This check is done in the overriddenauthenticate(Authentication authentication)method of theAuthenticationManagerinterface.
-
Spring Security after successful authentication
-
After successful authentication Spring security deletes secret data and credentials from
Authenticationobject. For reference - https://github.com/spring-projects/spring-security/blob/main/core/src/main/java/org/springframework/security/authentication/ProviderManager.java#L216
-
After successful authentication Spring security store authentication object in the SecurityContext. For reference - https://github.com/spring-projects/spring-security/blob/main/web/src/main/java/org/springframework/security/web/authentication/AbstractAuthenticationProcessingFilter.java#L323

Spring security basic flow chart
