How to Inject Authenticationmanager Using Java Configuration in a Custom Filter

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...

  1. You could make your filter a bean (beware the automatic registration of Filter beans in Spring Boot) via a @Bean method in your WebSecurityConfig

  2. 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



Leave a reply



Submit