1

I have a recently created jhipster application with the following .yo-rc.json

{
    "generator-jhipster": {
    "baseName": "cmpayments",
    "packageName": "au.com.cmx.myapp",
    "packageFolder": "au/com/cmx/myapp",
    "authenticationType": "token",
    "hibernateCache": "no",
    "clusteredHttpSession": "no",
    "websocket": "no",
    "databaseType": "sql",
    "devDatabaseType": "postgresql",
    "prodDatabaseType": "postgresql",
    "useCompass": false,
    "buildTool": "maven",
    "frontendBuilder": "gulp",
    "javaVersion": "8"
  }
}

I like having the token based authentication on the webapp but I'd like the server to expose a REST api call with just http basic authentication. I've been battling with for a while but I'm completely new to Spring security and I'm hoping someone has already done this and can help me out.

I tried following the solution here: Basic and form based authentication with Spring security Javaconfig

I created a second configuration with @Order(1) in SecurityConfiguration.java like so

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


    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication().withUser("api").password("pass").roles("API");
    }

    protected void configure(HttpSecurity http) throws Exception {
        http
            .csrf()
            .disable()
            .authorizeRequests()
            .antMatchers("/basicAuthApi/**").hasRole("API")
            .and()
            .httpBasic();
    }
}

This works. If I hit an endpoint under /basicAuthApi with anything other than api/pass credentials, I get a 401. Yay.

However, after this, when I log in to the webapp as admin/admin (or user/user), I get logged in as anonymousUser. If I comment out the extra @Configuration in SecurityConfiguration.java and restart the app, that problem goes away and I get logged in correctly as admin (or user).

Interestingly, I tried changing the order of the second @Configuration to @Order(101) because I saw somewhere in one of the base classes an @Order(100). In this case the admin and user logins on the webapp work. But the rest api call is no longer secure i.e it succeeds even with incorrect password.

Does anyone know what I am doing wrong?

Thanks dalyc

Community
  • 1
  • 1
dalyc
  • 33
  • 1
  • 4

3 Answers3

2

Replace the original SecurityConfiguration.configure :

http
        .csrf()
        .ignoringAntMatchers("/websocket/**")
    .and()
        .addFilterAfter(new CsrfCookieGeneratorFilter(), CsrfFilter.class)
        .exceptionHandling()
        .authenticationEntryPoint(authenticationEntryPoint)
    .and()
        .rememberMe()
        .rememberMeServices(rememberMeServices)
        .rememberMeParameter("remember-me")
        .key(env.getProperty("jhipster.security.rememberme.key"))
    .and()
        .formLogin()
        .loginProcessingUrl("/api/authentication")
        .successHandler(ajaxAuthenticationSuccessHandler)
        .failureHandler(ajaxAuthenticationFailureHandler)
        .usernameParameter("j_username")
        .passwordParameter("j_password")
        .permitAll()
    .and()
        .logout()
        .logoutUrl("/api/logout")
        .logoutSuccessHandler(ajaxLogoutSuccessHandler)
        .deleteCookies("JSESSIONID")
        .permitAll()
    .and()
        .headers()
        .frameOptions()
        .disable()
    .and()
        .authorizeRequests()
        .antMatchers("/api/register").permitAll()
        .antMatchers("/api/activate").permitAll()
        .antMatchers("/api/authenticate").permitAll()
        .antMatchers("/api/account/reset_password/init").permitAll()
        .antMatchers("/api/account/reset_password/finish").permitAll()
        .antMatchers("/api/logs/**").hasAuthority(AuthoritiesConstants.ADMIN)
        .antMatchers("/api/**").authenticated()
        .antMatchers("/metrics/**").hasAuthority(AuthoritiesConstants.ADMIN)
        .antMatchers("/health/**").hasAuthority(AuthoritiesConstants.ADMIN)
        .antMatchers("/trace/**").hasAuthority(AuthoritiesConstants.ADMIN)
        .antMatchers("/dump/**").hasAuthority(AuthoritiesConstants.ADMIN)
        .antMatchers("/shutdown/**").hasAuthority(AuthoritiesConstants.ADMIN)
        .antMatchers("/beans/**").hasAuthority(AuthoritiesConstants.ADMIN)
        .antMatchers("/configprops/**").hasAuthority(AuthoritiesConstants.ADMIN)
        .antMatchers("/info/**").hasAuthority(AuthoritiesConstants.ADMIN)
        .antMatchers("/autoconfig/**").hasAuthority(AuthoritiesConstants.ADMIN)
        .antMatchers("/env/**").hasAuthority(AuthoritiesConstants.ADMIN)
        .antMatchers("/trace/**").hasAuthority(AuthoritiesConstants.ADMIN)
        .antMatchers("/api-docs/**").hasAuthority(AuthoritiesConstants.ADMIN)
        .antMatchers("/protected/**").authenticated();

by this one :

http
        .csrf()
        .ignoringAntMatchers("/websocket/**")
    .and()
        .csrf()
        .ignoringAntMatchers("/basicAuthApi/**")
    .and()
        .addFilterAfter(new CsrfCookieGeneratorFilter(), CsrfFilter.class)
        .exceptionHandling()
        .authenticationEntryPoint(authenticationEntryPoint)
    .and()
        .rememberMe()
        .rememberMeServices(rememberMeServices)
        .rememberMeParameter("remember-me")
        .key(env.getProperty("jhipster.security.rememberme.key"))
    .and()
        .formLogin()
        .loginProcessingUrl("/api/authentication")
        .successHandler(ajaxAuthenticationSuccessHandler)
        .failureHandler(ajaxAuthenticationFailureHandler)
        .usernameParameter("j_username")
        .passwordParameter("j_password")
        .permitAll()
    .and()
        .logout()
        .logoutUrl("/api/logout")
        .logoutSuccessHandler(ajaxLogoutSuccessHandler)
        .deleteCookies("JSESSIONID")
        .permitAll()
    .and()
        .headers()
        .frameOptions()
        .disable()
    .and()
        .authorizeRequests()
        .antMatchers("/api/register").permitAll()
        .antMatchers("/api/activate").permitAll()
        .antMatchers("/api/authenticate").permitAll()
        .antMatchers("/api/account/reset_password/init").permitAll()
        .antMatchers("/api/account/reset_password/finish").permitAll()
        .antMatchers("/api/logs/**").hasAuthority(AuthoritiesConstants.ADMIN)
        .antMatchers("/api/**").authenticated()
        .antMatchers("/metrics/**").hasAuthority(AuthoritiesConstants.ADMIN)
        .antMatchers("/health/**").hasAuthority(AuthoritiesConstants.ADMIN)
        .antMatchers("/trace/**").hasAuthority(AuthoritiesConstants.ADMIN)
        .antMatchers("/dump/**").hasAuthority(AuthoritiesConstants.ADMIN)
        .antMatchers("/shutdown/**").hasAuthority(AuthoritiesConstants.ADMIN)
        .antMatchers("/beans/**").hasAuthority(AuthoritiesConstants.ADMIN)
        .antMatchers("/configprops/**").hasAuthority(AuthoritiesConstants.ADMIN)
        .antMatchers("/info/**").hasAuthority(AuthoritiesConstants.ADMIN)
        .antMatchers("/autoconfig/**").hasAuthority(AuthoritiesConstants.ADMIN)
        .antMatchers("/env/**").hasAuthority(AuthoritiesConstants.ADMIN)
        .antMatchers("/trace/**").hasAuthority(AuthoritiesConstants.ADMIN)
        .antMatchers("/api-docs/**").hasAuthority(AuthoritiesConstants.ADMIN)
        .antMatchers("/protected/**").authenticated()
    .and()
        .authorizeRequests()
        .antMatchers("/basicAuthApi/**")
        .hasAuthority(AuthoritiesConstants.USER).and().httpBasic();

I've only added :

.and()
        .csrf()
        .ignoringAntMatchers("/basicAuthApi/**")

and :

.and()
        .authorizeRequests()
        .antMatchers("/basicAuthApi/**")
        .hasAuthority(AuthoritiesConstants.USER).and().httpBasic()

You can also create a new authority which can only access these web services.

0

I found a solution that works for me. I realised that I don't need another @Configuration because the default jhipster configuration does not redirect to login page for unauthenticated access - it returns a 401 which is I want for the REST api too. So I just registered a user with the username and password that I want to use for the REST api and added the following line to the bottom of the configure method in

OAuth2ServerConfiguration.ResourceServerConfiguration

            `.antMatchers("/basicAuthApi/**").hasAuthority(AuthoritiesConstants.USER).and().httpBasic();`

I will now try to improve this by creating an API role and giving that role to users who need to call the REST API

I would still like to know why my original attempt had those problems if anyone knows.

Cheers dalyc

dalyc
  • 33
  • 1
  • 4
0

An intro regarding the order first: The default order if you do not specify one, is the biggest possible number which maps to the lowest possible priority (since lower order translates to higher priority). Therefore when you added the configuration with order=1, you had two configurations, with the new one with order=1 having higher priority and being checked first.

In the scenario you describe where both configurations exist the following happens: You try to log in to the webapp as admin/admin (or user/user), but spring security checks first the configuration with order=1 which has the ant matcher " .antMatchers("/basicAuthApi/**").hasRole("API")". It obviously does not match since the url you point is the one for the website, however the security does not fail because you are missing .anyRequest().authenticated() which would be needed in order to make the security check actually fail for users that failed to authenticate. Without this you actually pass the security check, although you are not authenticated i.e. you are considered an anonymous user with anonymous user access. Since spring security for that configuration succeeded, it does not even check the other one which is related to the website.

Marios
  • 1,947
  • 1
  • 15
  • 24