0

I have a spring project with working security configured, what I'm trying to do is set up a specific path that will accept REST calls with just basic user/password authentication, which can be hard-coded.

I know it is a bit of a weird case, but I have a very specific use-case for this.

The security code looks similar to:

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http
        ...
        .and()
            .authorizeRequests()
            .antMatchers("my-path/**").authenticated()
    }

I don't really understand how spring does all the magic, but I would have liked it to look something like:

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http
        ...
        .and()
            .authorizeRequests()
            .antMatchers("my-path/**").authenticatedWithUserPassword("user", "pswd")
    }

2 things that must happen:

  • I want this user/pswd to work for this path and this path only!
  • I want this path to work only for this user/pswd and for no other authentication types!
orirab
  • 2,915
  • 1
  • 24
  • 48
  • Do you have an other auth type in your API ? – Zorglube Apr 29 '19 at 12:21
  • yes, generally my app is protected with oauth2 – orirab Apr 29 '19 at 12:23
  • someone just referenced me to this: https://stackoverflow.com/questions/25794680/multiple-authentication-mechanisms-in-a-single-app-using-java-config , it seems like it might be the way to go – orirab Apr 29 '19 at 14:29
  • @orirab: The regular way is to use two Spring Security configurations, one matches only your special path (this configuration needs a lower order) and one matches all paths. Instead of using a global authentication manager you could use in your first Spring Security configuration a local in-memory authentication manager. – dur Apr 30 '19 at 22:14
  • @dur could you provide a code sample? – orirab May 01 '19 at 13:20

1 Answers1

0

Ok, so I did a bit of a hack (inspired by this answer) but it solved the problem for me.

First, I created this AuthenticationProvider:

public class ClusterInternalAuthenticationProvider implements AuthenticationProvider {

    public static final String USER = "...";
    public static final String PASSWORD = "...";

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        UsernamePasswordAuthenticationToken token = (UsernamePasswordAuthenticationToken)authentication;

        Object principal = token.getPrincipal();
        Object credentials = token.getCredentials();

        if (principal.equals(USER) && credentials.equals(PASSWORD)) {
            return new UsernamePasswordAuthenticationToken(
                principal,
                credentials,
                Collections.singletonList(new SimpleGrantedAuthority("RELEVANT_AUTHORITY"))
            );
        }

        throw new BadCredentialsException("Sorry mate, wrong credentials...");
    }

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

This tries the user/pswd combo and if true returns credentials with the authority I'll require for access to the specific path.

Next, in the SecurityConfiguration I enable httpBasic and add my AuthenticationProvider:

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http
        ...
        .and()
            .authorizeRequests()
            .antMatchers("my-path/**").hasAuthority("RELEVANT_AUTHORITY")
        .and()
            .httpBasic()
        .and()
            .authenticationProvider(new ClusterInternalAuthenticationProvider());
    }

This seems to be enough, but not a "correct" fix in the sense that I need to make sure not to use this authority elsewhere, and that it seems excessive for such a small need - other solutions are very welcome.

orirab
  • 2,915
  • 1
  • 24
  • 48