Spring Boot keycloak и базовая аутентификация вместе в одном проекте

У меня проблема с безопасностью Spring Boot. Я хочу иметь две разные аутентификации для одного и того же проекта одновременно в Spring Boot. Один из них - это SSO (аутентификация keycloak) для всех путей, кроме / download / export / *, другой - базовая аутентификация Spring Boot. Вот мой файл конфигурации:

@Configuration 
@EnableWebSecurityp 
public class MultiHttpSecurityConfig {
@Configuration
@Order(1)
public static class DownloadableExportFilesSecurityConfig extends WebSecurityConfigurerAdapter
{
@Override
protected void configure(HttpSecurity http) throws Exception
{
    http
            .antMatcher("/download/export/test")
            .authorizeRequests()
            .anyRequest().hasRole("USER1")
            .and()
            .httpBasic();    }

@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception
{
    auth.inMemoryAuthentication()
            .withUser("user").password("password1").roles("USER1");
}
}

@Configuration
@ComponentScan(basePackageClasses = KeycloakSecurityComponents.class)
public static class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter
{
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception
{
    auth.authenticationProvider(keycloakAuthenticationProvider());
}

@Bean
@Override
protected SessionAuthenticationStrategy sessionAuthenticationStrategy()
{
    return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
}

@Override
protected void configure(HttpSecurity http) throws Exception
{
    super.configure(http);
    http
            .regexMatcher("^(?!.*/download/export/test)")
            .authorizeRequests()
            .anyRequest().hasAnyRole("ADMIN", "SUPER_ADMIN")
            .and()
            .logout().logoutSuccessUrl("/bye");

}
}

Проблема с приведенным выше кодом заключается в следующем: если я запрашиваю url '/ download / export / test', он запрашивает у меня имя пользователя / пароль (обычная проверка подлинности). После успешного входа в систему он снова запрашивает у меня имя пользователя / пароль (но на этот раз аутентификация с помощью скрытого ключа), даже если запрошенный URL-адрес исключен из SecurityConfig (адаптер Keycloak).

Это дает мне только предупреждение:

2016-06-20 16:31:28.771  WARN 6872 --- [nio-8087-exec-6] o.k.a.s.token.SpringSecurityTokenStore   : Expected a KeycloakAuthenticationToken, but found org.springframework.security.authentication.UsernamePasswordAuthenticationToken@3fb541cc: Principal: org.springframework.security.core.userdetails.User@36ebcb: Username: user; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_USER1; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@957e: RemoteIpAddress: 127.0.0.1; SessionId: 4C1BD3EA1FD7F50477548DEC4B5B5162; Granted Authorities: ROLE_USER1

У вас есть идеи, как использовать скрытую клавиатуру и базовую аутентификацию вместе?

Огромное спасибо! Карло


person gubak    schedule 21.06.2016    source источник
comment
Возможный дубликат адаптера множественной аутентификации SpringBoot   -  person Jacob van Lingen    schedule 05.01.2017
comment
Есть ли новая информация по этой проблеме? Вы решили это?   -  person nejckorasa    schedule 12.10.2017


Ответы (2)


Объяснение проблемы

Проблема в том, что KeycloakAuthenticationProcessingFilter.java перехватывает каждый запрос с заголовком авторизации HTTP. Если ваш запрос не аутентифицирован с помощью Keycloak (даже если вы аутентифицированы с помощью любого другого поставщика аутентификации! - в вашем случае с базовой аутентификацией), вы всегда будете перенаправлены на страницу входа в Keycloak (в вашем case) или получите 401 Unauthorized (если ваш клиент Keycloak в keycloak.json настроен только на носитель).

По умолчанию KeycloakAuthenticationProcessingFilter.java вызывается, если запрос соответствует KeycloakAuthenticationProcessingFilter.DEFAULT_REQUEST_MATCHER:

public static final RequestMatcher DEFAULT_REQUEST_MATCHER =
    new OrRequestMatcher(
            new AntPathRequestMatcher(DEFAULT_LOGIN_URL),
            new RequestHeaderRequestMatcher(AUTHORIZATION_HEADER),
            new QueryParamPresenceRequestMatcher(OAuth2Constants.ACCESS_TOKEN)
    );

Это означает, что любой запрос, соответствующий DEFAULT_LOGIN_URL (/ sso / login) OR, содержит HTTP-заголовок авторизации (в вашем случае) OR имеет access_token в качестве параметра запроса будет обрабатываться KeycloakAuthenticationProcessingFilter.java.

Вот почему вы должны заменить RequestHeaderRequestMatcher(AUTHORIZATION_HEADER) своей собственной реализацией, которая будет пропускать вызов KeycloakAuthenticationProcessingFilter.java, когда запрос аутентифицируется с помощью базовой аутентификации.

Решение

Ниже представлено полное решение, которое позволяет одновременно использовать как обычную проверку подлинности, так и проверку подлинности Keycloak на одних и тех же путях. Обратите особое внимание на реализацию IgnoreKeycloakProcessingFilterRequestMatcher, которая заменяет стандартный RequestHeaderRequestMatcher. Этот сопоставитель будет соответствовать только запросам, содержащим HTTP-заголовок авторизации, значение которого не имеет префикса "Basic ".

В приведенном ниже примере пользователь с ролью TESTER может получить доступ к /download/export/test, в то время как все остальные пути доступны пользователям с ролями ADMIN или SUPER_ADMIN (которые, как я предполагаю, в вашем случае, являются учетными записями на сервере Keycloak).

@KeycloakConfiguration
public class MultiHttpSecurityConfig extends KeycloakWebSecurityConfigurerAdapter {

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
                .withUser("tester")
                .password("testerPassword")
                .roles("TESTER");
        auth.authenticationProvider(keycloakAuthenticationProvider());
    }

    @Bean
    @Override
    protected KeycloakAuthenticationProcessingFilter keycloakAuthenticationProcessingFilter() throws Exception {
        RequestMatcher requestMatcher =
                new OrRequestMatcher(
                        new AntPathRequestMatcher(DEFAULT_LOGIN_URL),
                        new QueryParamPresenceRequestMatcher(OAuth2Constants.ACCESS_TOKEN),
                        // We're providing our own authorization header matcher
                        new IgnoreKeycloakProcessingFilterRequestMatcher()
                );
        return new KeycloakAuthenticationProcessingFilter(authenticationManagerBean(), requestMatcher);
    }

    // Matches request with Authorization header which value doesn't start with "Basic " prefix
    private class IgnoreKeycloakProcessingFilterRequestMatcher implements RequestMatcher {
        IgnoreKeycloakProcessingFilterRequestMatcher() {
        }

        public boolean matches(HttpServletRequest request) {
            String authorizationHeaderValue = request.getHeader("Authorization");
            return authorizationHeaderValue != null && !authorizationHeaderValue.startsWith("Basic ");
        }
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        super.configure(http);
        http.authorizeRequests()
                .antMatchers("/download/export/test")
                .hasRole("TESTER")
                .anyRequest()
                .hasAnyRole("ADMIN", "SUPER_ADMIN")
                .and()
                .httpBasic();
    }

    @Bean
    @Override
    protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
        return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
    }
}
person Nejc Sever    schedule 06.02.2018
comment
Извините, но какую версию keycloak-spring-security-adapter вы используете? - person TheBakker; 11.06.2018
comment
Это пример для версии 3.4.3.Final. - person Nejc Sever; 12.06.2018
comment
С Spring-boot 2.3.0 я получаю эту ошибку: bean-компонент httpSessionManager, определенный в ресурсе пути к классу [net / ifao / companion / ccbd / config / KeycloakSecurityConfiguration.class], не может быть зарегистрирован. Компонент с таким именем уже определен в URL [jar: file: / C: /Users/wjose/.m2/repository/org/keycloak/keycloak-spring-security-adapter/5.0.0/keycloak-spring-security -adapter-5.0.0.jar! /org/keycloak/adapters/springsecurity/management/HttpSessionManager.class] и переопределение отключено. - person Winster; 05.06.2020
comment
используйте соответствующую версию keycloak, если вы используете spring -boot 2.3.0. Тогда вам нужно использовать 6.0.1 и более поздних версий mvnrepository.com /artifact/org.keycloak/ - person Puneeth Rai; 06.09.2020
comment
Отличное решение, спасибо :) У меня есть оптимизация для него, которая больше совместима с будущей версией: RequestMatcher requestMatcher = new AndRequestMatcher (KeycloakAuthenticationProcessingFilter.DEFAULT_REQUEST_MATCHER, // Мы предоставляем наш собственный сопоставитель заголовков авторизации new IgnoreKeycloakProcessingForBasicAuthenticationFilterRequest); - person Selim Ok; 15.05.2021
comment
Это не сработает для меня, если я не отключу enforcer для этого пути, но тогда он не отображает страницу входа в Spring для базовой аутентификации. У меня такие настройки: http. authenticationProvider(keycloakAuthenticationProvider()) .authorizeRequests() .antMatchers("/**").permitAll() .antMatchers(oAuth2AuthenticationSettings.getActuatorNonProtectedUrlPattern()).permitAll() .antMatchers(oAuth2AuthenticationSettings.getActuatorPrefix() + "/**").hasRole("ACTUATOR_USER") .and().httpBasic(); - person xbmono; 13.07.2021
comment
Можете ли вы проверить мои настройки (предыдущий комментарий) и посмотреть, не ошибся ли я? - person xbmono; 13.07.2021

Я решил это, настроив исключение в KeycloakAuthenticationProcessingFilter для пути:

...
@Configuration
@Order(2)
static class KeyCloakSecurityConfig extends KeycloakWebSecurityConfigurerAdapter {

@Bean
public KeycloakAuthenticationProcessingFilter keycloakAuthenticationProcessingFilter() throws Exception {
    KeycloakAuthenticationProcessingFilter filter = new KeycloakAuthenticationProcessingFilter(
            authenticationManagerBean()
            , new AndRequestMatcher(
               KeycloakAuthenticationProcessingFilter.DEFAULT_REQUEST_MATCHER,
               new NegatedRequestMatcher(new AntPathRequestMatcher(YOUR_BASIC_AUTHD_PATH))));
    filter.setSessionAuthenticationStrategy(sessionAuthenticationStrategy());
    return filter;
}
person kiskami    schedule 05.01.2018