SpringBoot多重身份验证适配器

时间:2016-06-20 11:14:22

标签: security spring-boot keycloak

我的Spring Boot Web应用程序中有一个非常特殊的要求: 我有内部和外部用户。内部用户使用keycloak身份验证登录Web应用程序(他们可以在Web应用程序中工作),但我们的外部用户通过简单的Spring Boot身份验证登录(他们可以做的只是下载Web应用程序生成的一些文件)

我想要做的是拥有多种身份验证模型: 除了/ download / *之外的所有路径都要通过我们的Keycloak身份验证进行身份验证,但路径/ download / *将通过SpringBoot基本身份验证进行身份验证。

目前我有以下内容:

@Configuration
@EnableWebSecurity
public class MultiHttpSecurityConfig {

    @Configuration
    @ComponentScan(basePackageClasses = KeycloakSecurityComponents.class)
    @Order(1)
    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");
        }

    }

    @Configuration
    @Order(2)
    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");
        }

    }
}

但它不能很好地工作,因为每次外部用户想要下载的东西(/ download / export / test),它都会提示登录表单,但输入正确的外部用户用户名和密码后,会提示输入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

你有什么想法吗?

2 个答案:

答案 0 :(得分:2)

在Keycloak身份验证旁边实现基本身份验证时遇到了一些令人头疼的问题,因为在执行“书籍”的多个WebSecurityAdapter实现时,即使基本身份验证成功,也会调用Keycloak身份验证过滤器。

原因在于: http://www.keycloak.org/docs/latest/securing_apps/index.html#avoid-double-filter-bean-registration

因此,如果您将Keycloak Spring安全适配器与Spring Boot一起使用,请确保添加这两个bean(除了Jacob von Lingen的有效答案):

@Configuration
@EnableWebSecurity
public class MultiHttpSecurityConfig {

    @Configuration
    @Order(1) //Order is 1 -> First the special case
    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)
    //no Order, will be configured last => All other urls should go through the keycloak adapter
    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());
        }

        // necessary due to http://www.keycloak.org/docs/latest/securing_apps/index.html#avoid-double-filter-bean-registration
        @Bean
        public FilterRegistrationBean keycloakAuthenticationProcessingFilterRegistrationBean(KeycloakAuthenticationProcessingFilter filter) {
            FilterRegistrationBean registrationBean = new FilterRegistrationBean(filter);
            registrationBean.setEnabled(false);
            return registrationBean;
        }
        // necessary due to http://www.keycloak.org/docs/latest/securing_apps/index.html#avoid-double-filter-bean-registration
        @Bean
        public FilterRegistrationBean keycloakPreAuthActionsFilterRegistrationBean(KeycloakPreAuthActionsFilter filter) {
            FilterRegistrationBean registrationBean = new FilterRegistrationBean(filter);
            registrationBean.setEnabled(false);
            return registrationBean;
        }


        @Override
        protected void configure(HttpSecurity http) throws Exception 
        {
            super.configure(http);
            http
                .authorizeRequests()
                .anyRequest().hasAnyRole("ADMIN", "SUPER_ADMIN")
                .and()
                .logout().logoutSuccessUrl("/bye");
        }

    }
}

答案 1 :(得分:1)

多个HttpSecurity的关键是注册特殊情况'在正常之前。换句话说,应在keycloak适配器之前注册/download/export/test身份验证适配器。

另一个需要注意的重要事项是,一旦身份验证成功,就不会调用其他适配器(因此不需要.regexMatcher("^(?!.*/download/export/test)"))。有关Multiple HttpSecurity的更多信息,请访问here

在您的代码下方进行最少的更改:

@Configuration
@EnableWebSecurity
public class MultiHttpSecurityConfig {

    @Configuration
    @Order(1) //Order is 1 -> First the special case
    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)
    @Order(2) //Order is 2 -> All other urls should go through the keycloak adapter
    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
                //removed .regexMatcher("^(?!.*/download/export/test)")
                .authorizeRequests()
                    .anyRequest().hasAnyRole("ADMIN", "SUPER_ADMIN")
                .and()
                    .logout().logoutSuccessUrl("/bye");
        }

    }
}