Reputation: 4691
I am currently migrating from Spring Security OAuth2 to Keycloak (after the Spring Security team's decision to deprecate the Spring Security OAuth2 project) but I am stuck with this exception:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'springSecurityFilterChain' defined in class path resource [org/springframework/security/config/annotation/web/configuration/WebSecurityConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [javax.servlet.Filter]: Factory method 'springSecurityFilterChain' threw exception; nested exception is java.lang.IllegalStateException: permitAll only works with HttpSecurity.authorizeRequests()
at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:656)
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:484)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1338)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1177)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:557)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:517)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:323)
at org.springframework.beans.factory.support.AbstractBeanFactory$$Lambda$330.00000000ED7F64A0.getObject(Unknown Source)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:321)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:310)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
I have 10 microservices to migrate, I defined a common
jar which contains all configurations. This is the Keycloak configuration in common
:
@EnableGlobalMethodSecurity( prePostEnabled = true, securedEnabled = true )
@KeycloakConfiguration
@Import( KeycloakSpringBootConfigResolver.class )
public class CommonsKeycloakSecurityConfigurerAdapter extends KeycloakWebSecurityConfigurerAdapter {
@Override
protected void configure( HttpSecurity http ) throws Exception {
http.oauth2ResourceServer(); // Equivalent to @EnableResourceServer in S.S. OAuth2
super.configure( http );
}
@Autowired
public void configureGlobal( AuthenticationManagerBuilder auth ) {
SimpleAuthorityMapper grantedAuthorityMapper = new SimpleAuthorityMapper();
grantedAuthorityMapper.setPrefix( "ROLE_" );
grantedAuthorityMapper.setConvertToUpperCase( true );
KeycloakAuthenticationProvider keycloakAuthenticationProvider = keycloakAuthenticationProvider();
keycloakAuthenticationProvider.setGrantedAuthoritiesMapper( grantedAuthorityMapper );
auth.authenticationProvider( keycloakAuthenticationProvider );
}
@Value("${spring.application.name}"
public String APP_NAME;
@Bean
public SessionAuthenticationStrategy sessionAuthenticationStrategyPorvider() {
return APP_NAME.equals( "securityservice")
? new RegisterSessionAuthenticationStrategy( new SessionRegistryImpl() )
: new NullAuthenticatedSessionStrategy();
}
@Bean
@DependsOn( "sessionAuthenticationStrategyPorvider" )
@Override
protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
return sessionAuthenticationStrategyPorvider();
}
@Bean
@Scope( scopeName = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS )
public KeycloakSecurityContext provideKeycloakSecurityContext() {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
Principal principal = requireNonNull( attributes ).getRequest().getUserPrincipal();
if ( principal == null ) {
return null;
}
if ( principal instanceof KeycloakAuthenticationToken ) {
principal = (Principal) ( (KeycloakAuthenticationToken) principal ).getPrincipal();
}
if ( principal instanceof KeycloakPrincipal ) {
return ( (KeycloakPrincipal) principal ).getKeycloakSecurityContext();
}
return null;
}
}
And properties in common/src/main/resources/application.yml
:
keycloak:
auth-server-url: "" #defined in application-dev.yml and application-prod.yml
realm: ${project.name}
resource: ${spring.application.name}
credentials:
secret: ${application.kck.secret}
use-resource-role-mappings: true
ssl-required: 'none'
principal-attribute: preferred_username
The common.jar
is then imported in all other microservices. In productservice
I have defined these specifique http security rules:
@Configuration
@Order( 1 )
public class UIResourceProtection extends WebSecurityConfigurerAdapter { // Note that I use WebSecurityConfigurerAdapter instead of KeycloakWebSecurityConfigurerAdapter
@Override
public void configure( HttpSecurity http ) throws Exception {
http.sessionManagement().sessionCreationPolicy( STATELESS );
http.requestMatchers().antMatchers( "/ui/product/**" )
.and()
.cors().and()
.authorizeRequests()
.antMatchers( "/ui/product/private").hasRole( "USER" )
.antMatchers( HttpMethod.PUT, "/ui/product/public" ).hasRole( "USER" )
.antMatchers( HttpMethod.GET, ""/ui/product/cost"","/ui/product/public").authenticated()
.antMatchers( HttpMethod.GET, "/ui/product/public/{id}" ).authenticated()
.antMatchers( "/ui/product/public/dashboard" ).hasRole( "USER" );
}
}
And another one in the same project (i-e: productservice
)
@Configuration
@RequiredArgsConstructor
@Order( 2 )
public class SelfResourceProtection extends WebSecurityConfigurerAdapter { // Note that I use WebSecurityConfigurerAdapter instead of KeycloakWebSecurityConfigurerAdapter
@Override
public void configure( HttpSecurity http ) throws Exception {
http.requestMatchers().antMatchers("/product/**")
.and()
.authorizeRequests()
.antMatchers( HttpMethod.GET, "/product/update-db" ).hasRole( "ADMIN" )
.antMatchers( HttpMethod.GET, "/product/private/{id}" ).permitAll();
}
}
When I start productservice
with that configuration I have the exception above: Caused by: java.lang.IllegalStateException: permitAll only works with HttpSecurity.authorizeRequests()
After changing configuration parameters, the exception is still thrown.
Any idea how to solve it?
Dependencies version :
Spring Boot : 2.2.4.RELEASE
Keycloak : 8.0.1
Spring Security: 5.2.1.RELEASE
Spring Security OAuth2 Resource Server: 5.2.1.RELEASE
Thanks a lot
Upvotes: 5
Views: 15787
Reputation: 19
http.authorizeRequests().antMatchers().permitAll()
use authorizeRequests not authorizeHttpRequests
Upvotes: 1
Reputation: 6489
This exception is caused by the class CommonsKeycloakSecurityConfigurerAdapter
.
If you look at the source code for KeycloakWebSecurityConfigurerAdapter
which you are extending, you will notice that part of the configuration states
.logoutUrl("/sso/logout").permitAll()
In order for permitAll()
to be applied, Spring Security expects authorizeRequests()
to be configured.
Since there is no such configuration in KeycloakWebSecurityConfigurerAdapter
, you will need to add an authorizeRequests()
block to your custom configuration that extends it.
Upvotes: 4