【问题标题】:SpringBoot multiple authentication adapterSpring Boot 多重身份验证适配器
【发布时间】:2016-10-21 15:00:33
【问题描述】:

我的 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

你有什么想法吗?

【问题讨论】:

    标签: security spring-boot keycloak


    【解决方案1】:

    多个 HttpSecurity 的关键是在正常情况之前注册“特殊情况”。也就是说,/download/export/test 认证适配器应该在 keycloak 适配器之前注册。

    需要注意的另一件重要事情是,一旦身份验证成功,就不会调用其他适配器(因此.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");
            }
    
        }
    }
    

    【讨论】:

    • 这不在我的项目中。当第一个 WebSecurityConfig(在我的情况下为基本安全)成功验证用户身份时,KeycloakWebSecurityConfigurerAdapter 仍会尝试对其进行身份验证并发送 401 响应以及 NOT_ATTEMPTED 代码。
    【解决方案2】:

    在实现 Keycloak 身份验证旁边的基本身份验证时,我感到有些头疼,因为仍然在“按规定”执行多个 WebSecurityAdapter 实现时,即使基本身份验证成功,也会调用 Keycloak 身份验证过滤器。

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

    因此,如果您将 Keycloak Spring Security Adapter 与 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");
            }
    
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2018-01-29
      • 2023-03-19
      • 2020-09-08
      • 2018-02-27
      • 2017-04-12
      • 2015-06-20
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多