How To Inject AuthenticationManager using Java Configuration in a Custom Filter
Override method authenticationManagerBean
in WebSecurityConfigurerAdapter
to expose the AuthenticationManager built using configure(AuthenticationManagerBuilder)
as a Spring bean:
For example:
@Bean(name = BeanIds.AUTHENTICATION_MANAGER)
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
Spring security custom filter (reason for creating AuthenticationManager bean while it already exists)
Spring uses AuthenticationManager
to find appropriate authentication provider but didn't register AuthenticationManager
as a bean by default. see the source code of HttpSecurityConfiguration
class.
@Bean(HTTPSECURITY_BEAN_NAME)
@Scope("prototype")
HttpSecurity httpSecurity() throws Exception {
WebSecurityConfigurerAdapter.LazyPasswordEncoder passwordEncoder = new WebSecurityConfigurerAdapter.LazyPasswordEncoder(
this.context);
AuthenticationManagerBuilder authenticationBuilder = new WebSecurityConfigurerAdapter.DefaultPasswordEncoderAuthenticationManagerBuilder(
this.objectPostProcessor, passwordEncoder);
authenticationBuilder.parentAuthenticationManager(authenticationManager());
HttpSecurity http = new HttpSecurity(this.objectPostProcessor, authenticationBuilder, createSharedObjects());
// @formatter:off
http
.csrf(withDefaults())
.addFilter(new WebAsyncManagerIntegrationFilter())
.exceptionHandling(withDefaults())
.headers(withDefaults())
.sessionManagement(withDefaults())
.securityContext(withDefaults())
.requestCache(withDefaults())
.anonymous(withDefaults())
.servletApi(withDefaults())
.apply(new DefaultLoginPageConfigurer<>());
http.logout(withDefaults());
// @formatter:on
return http;
}
private AuthenticationManager authenticationManager() throws Exception {
return (this.authenticationManager != null) ? this.authenticationManager
: this.authenticationConfiguration.getAuthenticationManager();
}
as you can see it register an authentication manager builder in HttpSecurity
. later in HttpSecurity
class it will use this builder to build an authentication manager and register it in shared objects [see method beforeConfigure()
in HttpSecurity
].
@Override
protected void beforeConfigure() throws Exception {
setSharedObject(AuthenticationManager.class, getAuthenticationRegistry().build());
}
and whenever it needs authentication manager uses HttpSecurity
to get that, like the following:
AuthenticationManager authenticationManager = http.getSharedObject(AuthenticationManager.class);
Cannot pass AuthenticationManager to custom filter by @Autowired
Your WebSecurityConfig
explicitly requests JWTLoginFilter
to be injected in it, and JWTLoginFilter
requests AuthenticationManager
to be injected in its constructor. AuthenticationManager
is supplied by WebSecurityConfig
, so you have a circular dependency.
Remove @Component
annotation from JWTLoginFilter
and define the filter as a bean in WebSecurityConfig
:
@Bean
public JWTLoginFilter jwtLoginFilter() {
return new JWTLoginFilter("/login", authenticationManager());
}
You will probably also need to inject UserService
manually in this method (for example, via constructor).
How to create a custom authentication filter in Spring Security?
Edit: This is the not best way to do things. It doesn't follow best practices. I haven't had time to udpate this answer with an example that does follow best practices.
As others have pointed out, it's better to use Basic auth or OAuth2, both of which are built into Spring. But if you really want to implement a custom filter, you can do something like this. (Please correct me if I'm doing this wrong.) But don't do this exactly. This is not a very secure example; it's a simple example.
class CustomAuthenticationFilter(val authManager: AuthenticationManager) : OncePerRequestFilter() {
override fun doFilterInternal(request: HttpServletRequest,
response: HttpServletResponse,
chain: FilterChain) {
val username = request.getHeader("foo_username")
val password = request.getHeader("foo_password")
if(username==null && password==null){
// not our responsibility. delegate down the chain. maybe a different filter will understand this request.
chain.doFilter(request, response)
return
}else if (username==null || password==null) {
// user is clearly trying to authenticate against the CustomAuthenticationFilter, but has done something wrong.
response.status = 401
return
}
// construct one of Spring's auth tokens
val authentication = UsernamePasswordAuthenticationToken(username, password, ArrayList())
// delegate checking the validity of that token to our authManager
val userPassAuth = this.authManager.authenticate(authRequest)
// store completed authentication in security context
SecurityContextHolder.getContext().authentication = userPassAuth
// continue down the chain.
chain.doFilter(request, response)
}
}
Once you've created your auth filter, don't forget to add it to your HttpSecurity config, like this:
override fun configure(http: HttpSecurity?) {
http!!.addFilterBefore(CustomAuthenticationFilter(authenticationManager()), UsernamePasswordAuthenticationFilter::class.java)
}
Spring Boot - Custom Authentication Filter without using the WebSecurityConfigurerAdapter
You can inject a bean of type AuthenticationManagerBuilder
into your SecurityConfiguration
and utilize its getOrBuild()
method, which doesn't raise exceptions, to get an instance of AuthenticationManager
.
So, your SecurityConfiguration
might look like this:
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfiguration {
private final UserDetailsService userDetailsService;
private final BCryptPasswordEncoder bCryptPasswordEncoder;
private final AuthenticationManagerBuilder authManagerBuilder;
// some beans
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests().anyRequest().permitAll()
.and()
.addFilter(new CustomAuthenticationFilter(authManagerBuilder.getOrBuild()));
return http.build();
}
}
AuthenticationManager is null in my custom filter
Your CustomUsernamePasswordAuthenticationFilter
isn't managed by Spring (since you created it directly) and so there is no Spring-managed dependency injection behavior in your filter. That's why the AuthenticationManager
was never injected and is now null.
Assuming you expose your AuthenticationManager
as a bean...
You could make your filter a bean (beware the automatic registration of
Filter
beans in Spring Boot) via a@Bean
method in yourWebSecurityConfig
Or you could simply pass in the
AuthenticationManager
to your filter (via its constructor or a setter) when you create the filter object. There's no need to have expose your filter as a bean.
Spring Security exposing AuthenticationManager without WebSecurityConfigurerAdapter
If you want the AuthenticationManager bean to be in the spring context, you can use the following solution.
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
return authenticationConfiguration.getAuthenticationManager();
}
This approach has solved the problem for me and you can inject AuthenticationManager wherever you need.
Related Topics
The Difference Between the Runnable and Callable Interfaces in Java
"Java.Lang.Outofmemoryerror:Unable to Create New Native Thread"
How to Define a Relative Path in Java
Most Efficient Way to Increment a Map Value in Java
Maven - Always Download Sources and Javadocs
Spring: How to Inject an Httpservletrequest into a Request-Scoped Bean
How to Connect to SQL Server Using Windows Authentication from Java Ee Webapp
Accessing a Variable from Another Class
Advanced Formatted Text Field Input Manipulation
Likelihood of Collision Using Most Significant Bits of a Uuid in Java
What's the Difference Between Getpath(), Getabsolutepath(), and Getcanonicalpath() in Java
How to Set Up Jax-Rs Application Using Annotations Only (No Web.Xml)
Why Isn't There a Java.Lang.Array Class? If a Java Array Is an Object, Shouldn't It Extend Object
Using Java with Nvidia Gpus (Cuda)
Use Cases and Examples of Gof Decorator Pattern for Io
How to Launch a Completely Independent Process from a Java Program