-
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).
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.
-
UserDetails is an interface that holds the user's details. e.g Name, Id, GrantedAuthority, Account Expiry, Account Locked, Password, etc.
-
UserDetails
interface is implemented in the Users class provided by Spring also it implements another interface called CredentialContainer. -
UserDetails
type is used in the different implementations of UserDetailsManager which wraps the user's info. -
There are different types of
UserDetailsManager
implementation such as InMemoryUserDetailsManager, and JdbcUserDetailsManager and it can be implemented to define a custom user details manager. -
The
UserDetailsManager
interface 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 theUserDetailsService
interface 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. -
AbstractUserDetailsAuthenticationProvider
class implements AuthenticationProvider interface which has authenticate(Authentication authentication) method. -
AbstractUserDetailsAuthenticationProvider
provides 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.
-
AuthenticationManager
has a couple of implementations; one of the concrete implementations is ProviderManager. -
ProviderManager class implements the authenticate(Authentication authentication) method calling the
AuthenticationProvider
in the runtime. -
AuthenticationManager
implementation is called by AuthenticationFilter class which creates anAuthentication
object 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
Authentication
type. -
There are different types of Spring-provided filters, one of the filters is UsernamePasswordAuthenticationFilter which makes requests to the implementation of the
AuthenticationManager
interface for all the configuredAuthenticationProvider
implementations. -
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.Filter
and 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
UsernamePasswordAuthenticationFilter
for the form-based login, this class extends AbstractAuthenticationProcessingFilter that extends GenericFilterBean andGenericFilterBean
implementsjavax.servlet.Filter
.UsernamePasswordAuthenticationFilter
overridesattemptAuthentication(HttpServletRequest request, HttpServletResponse response)
and it creates default UsernamePasswordAuthenticationToken object that is indirectly Authentication type. Below is the reference
-
AuthenticationProvider
in 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
AbstractAuthenticationProcessingFilter
and 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
ProviderManager
scan thru all the registered authentication provider and then execute that authentication mechanism. This check is done in the overriddenauthenticate(Authentication authentication)
method of theAuthenticationManager
interface.
-
-
After successful authentication Spring security deletes secret data and credentials from
Authentication
object. 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