Как решить `allowAll работает только с HttpSecurity.authorizeRequests ()` при миграции из Spring Security OAuth2 в Keycloak? - PullRequest
2 голосов
/ 26 января 2020

В настоящее время я выполняю миграцию из Spring Security OAuth2 в Keycloak (после решения команды Spring Security об отказе от поддержки проекта Spring Security OAuth2), но я застрял с этим исключением:

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)

У меня есть 10 микросервисов для мигрировать, я определил common банку, которая содержит все конфигурации. Это конфигурация Keycloak в 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;
}

}

И свойства в 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

common.jar затем импортируется в все остальные микросервисы. В productservice я определил эти конкретные правила безопасности http:

@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" );                    
    }
}

И еще один в том же проекте (т. Е. 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();     
    }
}

Когда я запускаю productservice с этой конфигурацией у меня есть исключение выше: Caused by: java.lang.IllegalStateException: permitAll only works with HttpSecurity.authorizeRequests()

После изменения параметров конфигурации исключение по-прежнему выдается.

Есть идеи, как ее решить?

Версия зависимостей:

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

Большое спасибо

1 Ответ

2 голосов
/ 27 января 2020

Это исключение вызвано классом CommonsKeycloakSecurityConfigurerAdapter.
Если вы посмотрите на исходный код для KeycloakWebSecurityConfigurerAdapter, который вы расширяете, вы заметите, что часть состояний конфигурации

.logoutUrl("/sso/logout").permitAll()

Чтобы применить permitAll(), Spring Security ожидает настройки authorizeRequests().

Поскольку в KeycloakWebSecurityConfigurerAdapter такой конфигурации нет, вам необходимо добавить в свою пользовательскую конфигурацию блок authorizeRequests(), расширяющий его.

...