user2912611
user2912611

Reputation: 544

Multiple custom authentication with spring security

I have a spring application which uses a custom Authentication Filter say filter1 to authorize the request, this filter uses an authentication manager for authentication and is applicable for all urls in application.

Now, I want to implement a different Authentication Filter say filter2 which has to authorize special kind of request say with url (/api/). That is the all the request which has the url like (/api/**) has to use filter2.

Below is the code I've tried so far for this purpose.

public class SecurityAppConfig {

@Configuration
@Order(1)
public static class APISecurityConfig extends WebSecurityConfigurerAdapter {

private CustomAuthenticationManager1 manager1 = new CustomAuthenticationManager1();

@Override
protected void configure(HttpSecurity http) throws Exception {
  http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
      .formLogin().disable().csrf().disable().cors().disable().logout().disable();

  if (manager1 != null) {
    http.addFilterAfter(new Filter1(manager1),
        AnonymousAuthenticationFilter.class);

  }

}
}


@Configuration
@Order(2)
public static class OtherApiSecurityConfig extends WebSecurityConfigurerAdapter {

private AuthenticationManager2 manager2 = new AuthenticationManager2();

@Override
protected void configure(HttpSecurity http) throws Exception {
  http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
      .formLogin().disable().csrf().disable().cors().disable().logout().disable();

  if (manager2 != null) {
    http.antMatchers("/api/**").addFilterAfter(new Filter2(manager2),
        AnonymousAuthenticationFilter.class);
  }
}
}

}

At the time of app start up both the filter are getting registered with their manager but when this ("/api/**") request comes it goes to the first filter for authentication but never goes to the second filter. If I remove the first filter then it works properly but that would override the filters for other api request.

Below is how I've implemented managers and filters

public class Filter1 extends AbstractAuthenticationProcessingFilter {
  //implementation omitted for brevity.
}

public class Filter2 extends AbstractAuthenticationProcessingFilter {
  //implementation omitted for brevity.
}

public class AuthenticationManager1 implements AuthenticationManager {
   //implementation omitted for brevity.
}

 public class AuthenticationManager2 implements AuthenticationManager {
   //implementation omitted for brevity.
}

Any thoughts on how can I get this working.

Upvotes: 1

Views: 4571

Answers (1)

Leffchik
Leffchik

Reputation: 2030

I don't think that you need two configs for your case. And I don't see why you need to implement your own authentication manager, even two of them. I guess you should use shared authentication manager instead, implement your own AuthenticationProvider (one for each type of authentication), and implement youe own authentication tokens. Besides that, since you're using AbstractAuthenticationProcessingFilter as a base class for you filters - you can set filterProcessesUrl into it, so your filter knows to which URL's it should be applied. So, in brief:

Authentication Tokens:

public class MyAuth1AuthenticationToken extends AbstractAuthenticationToken {
    // Implementation depends on you auth scheme (you can look on 
    // `UsernamePasswordAuthenticationToken` for example)
}

public class MyAuth2AuthenticationToken extends AbstractAuthenticationToken {
    // ...
}

Authentication Providers:

public class MyAuth1AuthenticationProvider implements AuthenticationProvider {
    @Override
    public Authentication authenticate(Authentication authentication)
        throws AuthenticationException {
        // Implementation really depends on you auth scheme (you can look on 
        // `AbstractUserDetailsAuthenticationProvider` for example)
    }        

    @Override
    public boolean supports(Class<?> authentication) {
        // By this we're saying that this auth provider is responsible for our MyAuth1 auth request 
        return (MyAuth1AuthenticationToken.class.isAssignableFrom(authentication));
    }
}

public class MyAuth2AuthenticationProvider implements AuthenticationProvider {
    @Override
    public Authentication authenticate(Authentication authentication)
        throws AuthenticationException {
        // ...
    }        

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

Filters:

public class Auth1Filter extends AbstractAuthenticationProcessingFilter {

    public Auth1Filter(AuthenticationManager authManager, String defaultFilterProcessesUrl) {
        super(defaultFilterProcessesUrl);
        setAuthenticationManager(authManager);
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request,
        HttpServletResponse response) throws AuthenticationException {

        // extract user info here
        // ...

        // populate auth request with your info
        MyAuth1AuthenticationToken authRequest = new MyAuth1AuthenticationToken(...);

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

public class Auth2Filter extends AbstractAuthenticationProcessingFilter {

    public Auth2Filter(AuthenticationManager authManager, String defaultFilterProcessesUrl) {
        super(defaultFilterProcessesUrl);
        setAuthenticationManager(authManager);
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request,
        HttpServletResponse response) throws AuthenticationException {

        // extract user info here
        // ...

        // populate auth request with your info
        MyAuth2AuthenticationToken authRequest = new MyAuth1AuthenticationToken(...);

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

Security Config:

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
public void configure(AuthenticationManagerBuilder auth) throws Exception
{
    // registering our providers
    auth
        .authenticationProvider(new MyAuth1AuthenticationProvider())
        .authenticationProvider(new MyAuth2AuthenticationProvider());   
}

    @Override
    protected void configure(HttpSecurity http) throws Exception {
       http
           .sessionManagement()
           .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
           .and()
           .formLogin().disable()
           .csrf().disable()
           .cors().disable()
           .logout().disable();

        AuthenticationManager authManager = http.getSharedObject(AuthenticationManager.class);

        http.addFilterAfter(new Auth1Filter(authManager, "/**"), BasicAuthenticationFilter.class);
        http.addFilterAfter(new Auth2Filter(authManager, "/api/**"), BasicAuthenticationFilter.class);
    }
}

Hope it helps.

Upvotes: 6

Related Questions