Spring-boot: Cannot configure OAuth2 resource server protected paths

Created on 28 Oct 2015  路  5Comments  路  Source: spring-projects/spring-boot

This should work:

@SpringBootApplication
@EnableResourceServer
public class SampleSecureOAuth2ResourceApplication extends ResourceServerConfigurerAdapter {

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.antMatcher("/api/**");
    }

    public static void main(String[] args) {
        SpringApplication.run(SampleSecureOAuth2ResourceApplication.class, args);
    }

}

but with application.properties:

security.oauth2.resource.id=service
security.oauth2.resource.tokenInfoUri=http://localhost:8080/oauth/check_token

you get this:

Caused by: java.lang.IllegalStateException: Cannot apply org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer@603fb93a to already built object
    at org.springframework.security.config.annotation.AbstractConfiguredSecurityBuilder.add(AbstractConfiguredSecurityBuilder.java:194)
    at org.springframework.security.config.annotation.AbstractConfiguredSecurityBuilder.apply(AbstractConfiguredSecurityBuilder.java:129)
    at org.springframework.security.config.annotation.web.builders.HttpSecurity.getOrApply(HttpSecurity.java:1187)
    at org.springframework.security.config.annotation.web.builders.HttpSecurity.authorizeRequests(HttpSecurity.java:592)
    at org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer.configure(ResourceServerSecurityConfigurer.java:199)
    at org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer.configure(ResourceServerSecurityConfigurer.java:1)
    at org.springframework.security.config.annotation.AbstractConfiguredSecurityBuilder.configure(AbstractConfiguredSecurityBuilder.java:378)
    at org.springframework.security.config.annotation.AbstractConfiguredSecurityBuilder.doBuild(AbstractConfiguredSecurityBuilder.java:328)
    at org.springframework.security.config.annotation.AbstractSecurityBuilder.build(AbstractSecurityBuilder.java:41)
    at org.springframework.security.config.annotation.web.builders.WebSecurity.performBuild(WebSecurity.java:289)
    at org.springframework.security.config.annotation.web.builders.WebSecurity.performBuild(WebSecurity.java:74)
    at org.springframework.security.config.annotation.AbstractConfiguredSecurityBuilder.doBuild(AbstractConfiguredSecurityBuilder.java:332)
    at org.springframework.security.config.annotation.AbstractSecurityBuilder.build(AbstractSecurityBuilder.java:41)
    at org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration.springSecurityFilterChain(WebSecurityConfiguration.java:105)
    at org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration$$EnhancerBySpringCGLIB$$f51585e6.CGLIB$springSecurityFilterChain$3(<generated>)
    at org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration$$EnhancerBySpringCGLIB$$f51585e6$$FastClassBySpringCGLIB$$7cac89cc.invoke(<generated>)
    at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:228)
    at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:318)
    at org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration$$EnhancerBySpringCGLIB$$f51585e6.springSecurityFilterChain(<generated>)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)
    at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:162)
    ... 39 more

The application.psoperties are missing client credentials, so it should fail at some point, but not with this confusing error.

declined

Most helpful comment

The problem appears in Spring Security OAuth's ResourceServerSecurityConfigurer which is trying to add an additional SecurityConfigurer in the configure phase. This is not permitted because the contract is:

  • All SecurityConfigurer.init methods are invoked
  • All SecurityConfigurer.configure methods are invoked

However, in this instance http.authorizeRequests() is trying to add another SecurityConfigurer in the ResourceServerSecurityConfigurer.configure method. This breaks the contract since the newly added SecurityConfigurer needs its init method invoked, but we have already invoked at least one configure method.

Likely the reason this has gone unnoticed is if someone has already configured the 'http.authorizeRequests()then the error is not present (since theSecurityConfigurer` is not being added but modified). For example, the following fixes the error:

@SpringBootApplication
@EnableResourceServer
public class SampleSecureOAuth2ResourceApplication extends ResourceServerConfigurerAdapter {

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http
            .antMatcher("/api/**")
            // Add below
            .authorizeRequests()
                .anyRequest().authenticated();
    }

    public static void main(String[] args) {
        SpringApplication.run(SampleSecureOAuth2ResourceApplication.class, args);
    }

}

Ultimately, Spring Security OAuth should be using the init method to invoke http.authorizeRequests is going to be added.

All 5 comments

Changing the application.properties slightly you get a different problem:

security.oauth2.client.client-id=foo
security.oauth2.client.client-secret=bar
security.oauth2.resource.id=service
security.oauth2.resource.tokenInfoUri=http://localhost:8080/oauth/check_token

or

security.oauth2.resource.id=service
security.oauth2.resource.userInfoUri=http://localhost:8080/user
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.web.servlet.HandlerMapping]: Factory method 'viewControllerHandlerMapping' threw exception; nested exception is java.lang.IllegalStateException: The resources may not be accessed if they are not currently started
    at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:189)
    at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:588)
    ... 18 more
Caused by: java.lang.IllegalStateException: The resources may not be accessed if they are not currently started
    at org.apache.catalina.webresources.StandardRoot.validate(StandardRoot.java:245)
    at org.apache.catalina.webresources.StandardRoot.getResource(StandardRoot.java:212)
    at org.apache.catalina.webresources.StandardRoot.getResource(StandardRoot.java:206)

Update: it's the same problem in both cases. In the second example the failure (same one as first example) has happened in a different thread and the servlet container has failed to start so the exception bubbles up differently.

The problem appears in Spring Security OAuth's ResourceServerSecurityConfigurer which is trying to add an additional SecurityConfigurer in the configure phase. This is not permitted because the contract is:

  • All SecurityConfigurer.init methods are invoked
  • All SecurityConfigurer.configure methods are invoked

However, in this instance http.authorizeRequests() is trying to add another SecurityConfigurer in the ResourceServerSecurityConfigurer.configure method. This breaks the contract since the newly added SecurityConfigurer needs its init method invoked, but we have already invoked at least one configure method.

Likely the reason this has gone unnoticed is if someone has already configured the 'http.authorizeRequests()then the error is not present (since theSecurityConfigurer` is not being added but modified). For example, the following fixes the error:

@SpringBootApplication
@EnableResourceServer
public class SampleSecureOAuth2ResourceApplication extends ResourceServerConfigurerAdapter {

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http
            .antMatcher("/api/**")
            // Add below
            .authorizeRequests()
                .anyRequest().authenticated();
    }

    public static void main(String[] args) {
        SpringApplication.run(SampleSecureOAuth2ResourceApplication.class, args);
    }

}

Ultimately, Spring Security OAuth should be using the init method to invoke http.authorizeRequests is going to be added.

So the sample is fixed (spring-boot-sample-secure-oauth2-resource) by explicitly authorizing all requests as per the snippet Rob gave. I don't pretend to understand why that worked yet, but it seems reasonable that most people will want to do that. And the problem is in spring-security-oauth2 anyway, so I'll mark this as "On Hold" in case anyone wants to track the Boot side of it.

Closing since Spring Boot 2.0 provides OAuth support via Spring Security

Was this page helpful?
0 / 5 - 0 ratings