Spring boot oauth2 management httpbasic authentica

2019-06-22 01:34发布

问题:

I've got a spring boot application that uses oauth2 for authentication. The oauth2 mechanism is working and clients can authenticate and receive their access tokens.

I want to secure the actuators endpoints with httpbasic authentication, i.e. not requiring the user to first use oauth2 for authentication and then access the actuator endpoints. What i've done so far is to set the following in properties file:

management.context-path=/admin/actuators
management.security.enabled=true
management.security.role=ADMIN

security.user.name=admin
security.user.password=password

I've tried various ways to set configuration with a ResourceServerConfigurerAdapter and WebSecurityConfigurerAdapter.

None of my attempts are working and it keeps on telling me

<oauth>
<error_description>
Full authentication is required to access this resource
</error_description>
<error>unauthorized</error>
</oauth>

What is the correct way to get OAUTH2 and the management endpoint to work?

回答1:

The problem is that @EnableResourceServer imports ResourceServerConfiguration, which has an order of 3, far superior to ManagementServerProperties.ACCESS_OVERRIDE_ORDER.
See Spring Boot documentation on actuator security and ordering config classes : http://docs.spring.io/spring-boot/docs/1.4.3.RELEASE/reference/htmlsingle/#boot-features-security-actuator

The default actuator security config is a lot more clever than just allowing access to the /health endpoint and blocking the rest, it actually changes depending on management.port and management.contextPath, and it can get pretty hard to find the correct management endpoint URLs without leaving gaping holes in your security or messing with your own resources.

If you want to keep the benefit of the autoconfigured management security, two options :

EDIT : a) Lower ResourceServerConfiguration order using a BeanPostProcessor

This improvement has been suggested by @dsyer on the github thread :

@Component
@Slf4j
public class ResourceServerConfigurationPostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof ResourceServerConfiguration) {
            LOGGER.debug("Lowering order of ResourceServerConfiguration bean : {}", beanName);
            ResourceServerConfiguration config = (ResourceServerConfiguration) bean;
            config.setOrder(SecurityProperties.ACCESS_OVERRIDE_ORDER);
        }
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

}

I just replaced my code below with this class, and it works perfectly.


EDIT : b) Manually overriding ResourceServerConfiguration order

If you don't like post processors for some reason, you can replace the @EnableResourceServer with another configuration class whose order will come after the default management security :

/** 
 * Extend the default resource server config class, and downgrade its order
 */
public class ResourceServerLowPrecedenceConfiguration extends ResourceServerConfiguration {

     /**
     * This is enough to override Spring Boot's default resource security,
     * but it does not takes over the management.
     */
    @Override
    public int getOrder() {
        return SecurityProperties.ACCESS_OVERRIDE_ORDER;
    }
}

And your own configuration class :

/** @EnableResourceServer is replaced by @Import using the low precedence config */
@Configuration
@Import(ResourceServerLowPrecedenceConfiguration.class)
public class YourOwnOAuth2Config extends ResourceServerConfigurerAdapter {
    @Override
    public void configure(final HttpSecurity http) throws Exception {
        // Secure your resources using OAuth 2.0 here
    }
}

EDIT : You can also rewrite your own @EnableResourceServer annotation to shortcut the @Import :

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(ResourceServerLowPrecedenceConfiguration.class)
public @interface EnableResourceServer {
}

IMHO this should be the default behavior when spring-security-oauth is on the classpath.
See discussion on GitHub issue : https://github.com/spring-projects/spring-boot/issues/5072



回答2:

security.oauth2.resource.filter-order = 3 in application.yml will do the trick



回答3:

With Spring-Security you can have Multiple HttpSecurity configuration.

<http pattern="/actuators/**/*" request-matcher="ant" authentication-manager-ref="basicAuthManager">
    <security:intercept-url pattern="/**" access="isAuthenticated()" />
    <http-basic />
<http>
<http use-expressions="false">
   ... your oauth config
</http>

<authentication-manager id="basicAuthManager">
    <authentication-provider>
        <user-service>
            <user name="user1" password="user1Pass" authorities="ROLE_USER" />
        </user-service>
    </authentication-provider>
</authentication-manager>

... your oath config stuff

(I prefere xml, but you can do this with java config too)

@See http://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#nsa-http

(But think that you could not do this by plain spring-boot configuration.)



回答4:

Ok, got it to work using the following java config.

The endpoint, /admin/actuators/health, is accessible by anyone and all other /admin/actuators/* endpoints are authenticated.

@Configuration
@Order(1)
protected static class ApiWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
    protected void configure(HttpSecurity http) throws Exception {

        http
                .authorizeRequests()
                .antMatchers(HttpMethod.GET, "/admin/actuators/health").permitAll()
            .and()
                .antMatcher("/admin/actuators/**")
                .authorizeRequests()
                .anyRequest()
                .hasRole("ADMIN")
                .and()
                .httpBasic();
    }
}