Spring Security getAuthenticationManager() returns null within custom filter

Wolfone :

I am trying to implement a very simple example of customized authentication process in Spring to get a better understanding of the concept.

I thought i had everything in place now but sending a request to test what I implemented results in a NullPointerException which can be tracked down to this.getAuthenticationManager() returning null in my custom filter. But I don't understand why. The very similar existing question didn't really help me unfortunately. So I would be thankful for help; here are the most relevant classes I think, feel free to ask if any more is needed.

MyAuthenticationFilter (Based on the source-code of UsernamePasswordAuthenticationFilter), error happens in last line of this:

public class MyAuthenticationFilter extends AbstractAuthenticationProcessingFilter {

    public MyAuthenticationFilter() {
        super(new AntPathRequestMatcher("/login", "POST"));
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
            if (!request.getMethod().equals("POST")) {
                throw new AuthenticationServiceException(
                        "Authentication method not supported: " + request.getMethod());
            }

            String username = request.getParameter("username");
            String password = request.getParameter("password");
            String secondSecret = request.getParameter("secondSecret");

            if (username == null) {
                username = "";
            }

            if (password == null) {
                password = "";
            }

            username = username.trim();

            MyAuthenticationToken authRequest = new MyAuthenticationToken(username, new MyCredentials(password, secondSecret));

            return this.getAuthenticationManager().authenticate(authRequest);
    }
}

My config-class:

@Configuration
@EnableWebSecurity
@EnableWebMvc
@ComponentScan
public class AppConfig extends WebSecurityConfigurerAdapter
{
    @Autowired
    MyAuthenticationProvider myAuthenticationProvider;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(myAuthenticationProvider);
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception
    {
        http.addFilterBefore(new MyAuthenticationFilter(), BasicAuthenticationFilter.class)
                .authorizeRequests().antMatchers("/**")
                    .hasAnyRole()
                    .anyRequest()
                    .authenticated()
                    .and()
                .csrf().disable();
    }

    @Bean
    public ViewResolver viewResolver()
    {
        InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
        viewResolver.setPrefix("/WEB-INF/views/");
        viewResolver.setSuffix(".jsp");
        return viewResolver;
    }
}

MyAuthenticationProvider:

@Component
public class MyAuthenticationProvider implements AuthenticationProvider {
    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        MyAuthenticationToken myAuthenticationToken = (MyAuthenticationToken) authentication;
        MyCredentials credentials = (MyCredentials) myAuthenticationToken.getCredentials();
        if (credentials.getPassword().equals("sesamOeffneDich") && credentials.getSecondSecret().equals(MyAuthenticationToken.SECOND_SECRET)){
            myAuthenticationToken.setAuthenticated(true);
            return myAuthenticationToken;
        }else{
            throw new BadCredentialsException("Bad credentials supplied!");
        }
    }

    @Override
    public boolean supports(Class<?> authentication) {
        return MyAuthenticationToken.class.isAssignableFrom(authentication);
    }
}
shinjw :

Why you're seeing a NullPointerException

You're seeing a NullPointerException because you do not have an AuthenticationManager wired into your filter. According to the javadocs for AbstractAuthenticationProcessingFilter

The filter requires that you set the authenticationManager property. An AuthenticationManager is required to process the authentication request tokens created by implementing classes

This being the case, I do scratch my head as to why the authenticationManager did not make the cut for a constructor argument for this abstract filter. I would recommend enforcing this in your constructor for your custom filter.

public MyAuthenticationFilter(AuthenticationManager authenticationManager) {
    super(new AntPathRequestMatcher("/login", "POST"));
    this.setAuthenticationManager(authenticationManager);
}

AuthenticationManager or the AuthenticationProvider

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.authenticationProvider(myAuthenticationProvider);
}

The AuthenticationManagerBuilder will create a ProviderManager (an AuthenticationManager).

The ProviderManager is a collection of AuthenticationProvider and will attempt to authenticate() with every AuthenticationProvider that it manages. (This is where public boolean supports(Class<?> authentication) is extremely important in theAuthenticationProvider contract)

In your configuration, you have created an ProviderManager containing just your custom Authentication Provider

Cool. Now where is my AuthenticationManager?

It is possible to grab the AuthenticationManager built by the configure() method by this.authenticationManager() in the WebSecurityConfigurerAdapter

@Bean
public AuthenticationManager authenticationManager throws Exception() {
    this.authenticationManager();
}

Recommendations

Creating your own ProviderManager does have it's benefits as it would be explicit and within your control.

@Bean
public AuthenticationManager authenticationManager() {
    return new ProviderManager(Arrays.asList(myAuthenticationProvider));
}

This will let you be flexible with the placement of your AuthenticationManager bean and avoid:

  • Potential cyclic dependency issues in your bean configurations
  • Bubbling up the checked Exception as a result of calling this.authenticationManager()

Putting it all Together

...
public class AppConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    MyAuthenticationProvider myAuthenticationProvider;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.addFilterBefore(new MyAuthenticationFilter(authenticationManager()), BasicAuthenticationFilter.class)
        ...
    }

    @Bean
    public AuthenticationManager authenticationManager() {
        return new ProviderManager(Arrays.asList(myAuthenticationProvider));
    }
}

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=68680&siteId=1