【问题标题】:Spring Boot keycloak and basic authentication together in the same projectSpring Boot keycloak 和基本认证一起在同一个项目中
【发布时间】:2016-10-22 21:33:40
【问题描述】:

我遇到了 Spring Boot 安全问题。我想要的是在 Spring Boot 中同时对同一个项目进行两种不同的身份验证。一种是除 '/download/export/*' 之外的所有路径的 SSO(keycloak 身份验证),另一种是 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',它会询问我用户名/密码(基本身份验证)。成功登录后,它再次要求我输入用户名/密码(但这次是 keycloak 身份验证),即使请求的 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

您对如何同时使用 keycloak 和基本身份验证有任何想法吗?

非常感谢! 卡罗

【问题讨论】:

标签: spring spring-mvc authentication spring-security keycloak


【解决方案1】:

问题说明

您遇到的问题是KeycloakAuthenticationProcessingFilter.java 使用 HTTP 授权标头拦截每个请求。如果您的请求未通过 Keycloak 进行身份验证(即使您已通过任何其他身份验证提供商进行身份验证!-在您的情况下使用基本身份验证)您将总是被重定向到 Keycloak 的登录页面(在您的情况下)或获取 401 Unauthorized(如果您在 keycloak.json 中的 Keycloak 客户端配置为仅承载)。

如果请求匹配KeycloakAuthenticationProcessingFilter.DEFAULT_REQUEST_MATCHER,默认调用KeycloakAuthenticationProcessingFilter.java

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 的请求都包含 Authorization HTTP 标头(在您的情况下)OR 具有access_token 作为查询参数,将被KeycloakAuthenticationProcessingFilter.java 处理。

这就是为什么你必须用你自己的实现替换RequestHeaderRequestMatcher(AUTHORIZATION_HEADER),当请求通过基本身份验证时将跳过KeycloakAuthenticationProcessingFilter.java的调用。

解决方案

下面是一个完整的解决方案,使您能够在相同的路径上同时使用基本身份验证和 Keycloak 身份验证。请特别注意替换默认 RequestHeaderRequestMatcherIgnoreKeycloakProcessingFilterRequestMatcher 实现。此匹配器将仅匹配包含 Authorization HTTP 标头的请求,该标头的值不以 "Basic " 为前缀。

在下面的示例中,具有TESTER 角色的用户可以访问/download/export/test,而具有ADMINSUPER_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());
    }
}

【讨论】:

  • 抱歉,您使用的是哪个版本的 keycloak-spring-security-adapter?
  • 此示例适用于版本 3.4.3.Final。
  • 使用 spring-boot 2.3.0,我收到此错误,在类路径资源 [net/ifao/companion/ccbd/config/KeycloakSecurityConfiguration.class] 中定义的 bean 'httpSessionManager' 无法被注册。已在 URL [jar:file:/C:/Users/wjose/.m2/repository/org/keycloak/keycloak-spring-security-adapter/5.0.0/keycloak-spring-security 中定义了具有该名称的 bean -adapter-5.0.0.jar!/org/keycloak/adapters/springsecurity/management/HttpSessionManager.class] 并且覆盖被禁用。
  • 如果您使用的是 spring-boot 2.3.0,请使用匹配版本的 keycloak。然后你需要使用 6.0.1 及以上版本mvnrepository.com/artifact/org.keycloak/…
  • 非常好的解决方案,谢谢 :) 我对其进行了优化,它与未来的版本更兼容: RequestMatcher requestMatcher = new AndRequestMatcher(KeycloakAuthenticationProcessingFilter.DEFAULT_REQUEST_MATCHER, // 我们提供了我们自己的授权标头匹配器 new IgnoreKeycloakProcessingForBasicAuthenticationFilterRequestMatcher( ) );
【解决方案2】:

我通过在 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;
}

【讨论】:

    猜你喜欢
    • 2018-02-14
    • 1970-01-01
    • 2018-10-08
    • 2018-12-23
    • 2017-10-31
    • 2020-03-12
    • 2020-08-23
    • 2022-01-25
    相关资源
    最近更新 更多