繁体   English   中英

如何基于@Profile为spring-security添加过滤器?

[英]How do I add a filter to spring-security based on a @Profile?

我试图定义两个不同的bean(都扩展AbstractPreAuthenticatedProcessingFilter):一个用于在“开发”配置文件处于活动状态时从请求中获取头(例如USER_ID),另一个用于从请求头中获取JWT “开发”配置文件激活。 (从概念上讲,我真的只是尝试以编程方式根据bean本身的存在来注册过滤器)目前,我甚至都没有尝试使用配置文件,因为我遇到了自动获取标头的问题在适当的过滤链中注册。

该应用程序使用Spring-Boot 2.0.0.RELEASE,配置为使用嵌入式Tomcat,该服务注释为@RestController。 下面是我的SecurityConfig类的简化版本:

@Configuration
@EnableWebSecurity(debug=true)
@EnableGlobalMethodSecurity(prePostEnabled = true, jsr250Enabled = true, securedEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {


@Bean
public UserIdAuthenticationFilter UserIdAuthenticationFilter() throws Exception {
    ...
}

@Bean
public JwtAuthenticationFilter jwtAuthenticationFilter() throws Exception {
    ...
}


@Override
protected void configure(HttpSecurity http) throws Exception {

    http
        .cors().and()
        .csrf().disable()
        .headers().frameOptions().sameOrigin()
        .and()
        //.addFilter(jwtAuthenticationFilter())
        //.addFilter(UserIdAuthenticationFilter())
        .exceptionHandling()
        .and().authorizeRequests()
        .antMatchers("/", "/index.html", "/css/**", "/images/**", "/js/**").permitAll()
        .anyRequest()
        .authenticated()
        .antMatchers("/**").permitAll()
    ;


 }

}

正如你所看到的,我已经定义了我的过滤器bean,它们在应用于正确的链时起作用......我看到的问题是spring似乎在某处注册到过滤器,但是当我打电话给服务时端点,它从不调用我添加的过滤器代码。

运行应用程序的日志输出似乎表明过滤器正在定位...

 2018-04-04 09:43:02.907  INFO 7717 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'characterEncodingFilter' to: [/*]
 2018-04-04 09:43:02.907  INFO 7717 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'hiddenHttpMethodFilter' to: [/*]
 2018-04-04 09:43:02.907  INFO 7717 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'httpPutFormContentFilter' to: [/*]
 2018-04-04 09:43:02.908  INFO 7717 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'requestContextFilter' to: [/*]
 2018-04-04 09:43:02.908  INFO 7717 --- [ost-startStop-1] .s.DelegatingFilterProxyRegistrationBean : Mapping filter: 'springSecurityFilterChain' to: [/*]
 2018-04-04 09:43:02.908  INFO 7717 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'httpTraceFilter' to: [/*]
 2018-04-04 09:43:02.908  INFO 7717 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'webMvcMetricsFilter' to: [/*]
 2018-04-04 09:43:02.908  INFO 7717 --- [ LOOK HERE ] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'userIdAuthenticationFilter' to: [/*]
 2018-04-04 09:43:02.908  INFO 7717 --- [ LOOK HERE ] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'jwtAuthenticationFilter' to: [/*]
 2018-04-04 09:43:02.909  INFO 7717 --- [ost-startStop-1] o.s.b.w.servlet.ServletRegistrationBean  : Servlet dispatcherServlet mapped to [/]
 2018-04-04 09:43:02.922 DEBUG 7717 --- [ost-startStop-1] g.n.a.f.w.c.a.f.JwtAuthenticationFilter  : Initializing filter 'jwtAuthenticationFilter'
 2018-04-04 09:43:02.922 DEBUG 7717 --- [ost-startStop-1] g.n.a.f.w.c.a.f.JwtAuthenticationFilter  : Filter 'jwtAuthenticationFilter' configured successfully
 2018-04-04 09:43:02.922 DEBUG 7717 --- [ost-startStop-1] c.a.f.UserIdAuthenticationFilter : Initializing filter 'userIdAuthenticationFilter'
 2018-04-04 09:43:02.922 DEBUG 7717 --- [ost-startStop-1] c.a.f.UserIdAuthenticationFilter : Filter 'userIdAuthenticationFilter' configured successfully

现在应用程序正在运行,当我尝试访问该服务时,我看到spring转储以下输出(启用调试):

 Security filter chain: [
   WebAsyncManagerIntegrationFilter
   SecurityContextPersistenceFilter
   HeaderWriterFilter
   CorsFilter
   LogoutFilter
   RequestCacheAwareFilter
   SecurityContextHolderAwareRequestFilter
   AnonymousAuthenticationFilter
   SessionManagementFilter
   ExceptionTranslationFilter
   FilterSecurityInterceptor
   ]

鉴于输出,它表明,我认为安全过滤器链没有应用我的过滤器。

也许更有说服力的是,如果您在我的配置代码片段中注意到有两行被注释掉(这是我在尝试使用基于配置文件的检测之前手动添加过滤器的地方)。 如果我取消注释添加JWT过滤器的行(也就是手动注册过滤器,而不是依赖于检测),一切似乎都按预期工作。 查看调试输出,我现在在手动添加过滤器后调用端点时看到以下内容(注意JwtAuthenticationFilter现在出现在安全过滤器链中):

 Security filter chain: [
     WebAsyncManagerIntegrationFilter
     SecurityContextPersistenceFilter
     HeaderWriterFilter
     CorsFilter
     LogoutFilter
     JwtAuthenticationFilter <-----
     RequestCacheAwareFilter
     SecurityContextHolderAwareRequestFilter
     AnonymousAuthenticationFilter
     SessionManagementFilter
     ExceptionTranslationFilter
     FilterSecurityInterceptor
   ]

我问的确有两个问题......我显然不理解spring-boot / spring-security设置的各种过滤器链,所以在应用程序启动期间注册的bean之间的区别是什么?正常的过滤链,与使用spring-security过滤链注册的bean相比? 有人可以指出我做错了什么,为什么?

注册这些“可选”bean的正确方法是什么? (可选,因为可能不依赖于活动配置文件。)

谢谢!

从关于Spring Security架构的文章

Spring Security作为链中的单个Filter安装,其concerete类型是FilterChainProxy,原因很快就会显现出来。 在Spring Boot应用程序中,安全筛选器是ApplicationContext中的@Bean,默认情况下会安装它,以便将其应用于每个请求。

Spring Security可以在同一顶级FilterChainProxy中管理多个过滤器链,并且容器都是未知的。 Spring Security过滤器包含过滤器链列表,并将请求分派给与其匹配的第一个链。

还要注意:

Spring安全内部的所有过滤器都不为容器所知,这一点很重要,尤其是在Spring Boot应用程序中,默认情况下,所有类型为Filter的@Beans都会自动注册到容器中。 因此,如果要向安全链添加自定义筛选器,则需要将其设置为@Bean,或者将其包装在显式禁用容器注册的FilterRegistrationBean中。

因此,当您将过滤器定义为Spring bean时,它会自动注册到servlet容器,但不会注册Spring Security过滤器链。 这就是为什么你需要使用addFilter方法显式地将它添加到Spring Security链。 您还需要在servlet容器中禁用自动注册,否则将调用过滤器两次。

也可以看看:

至于配置文件,至少有两种方法可以满足您的需求:

  1. 扩展AbstractHttpConfigurer并在那里移动常见的安全配置。 之后,为每个配置文件创建单独的安全配置:

     @Configuration @EnableWebSecurity(debug = true) @EnableGlobalMethodSecurity(prePostEnabled = true, jsr250Enabled = true, securedEnabled = true) public class SecurityConfiguration { /** * Development security configuration. */ @Profile("dev") @Configuration public static class DevSecurityConfiguration extends WebSecurityConfigurerAdapter { @Bean public FilterRegistrationBean userIdAuthenticationFilter() { // ... } @Override protected void configure(HttpSecurity http) throws Exception { http.apply(commonSecurityConfiguration()) .and().addFilter(userIdAuthenticationFilter().getFilter()); } } /** * Production security configuration. */ @Profile("!dev") @Order(1) @Configuration public static class ProdSecurityConfiguration extends WebSecurityConfigurerAdapter { @Bean public FilterRegistrationBean jwtAuthenticationFilter() { // ... } @Override protected void configure(HttpSecurity http) throws Exception { http.apply(commonSecurityConfiguration()) .and().addFilter(jwtAuthenticationFilter().getFilter()); } } /** * Common security configuration reused by all profiles. */ public static class CommonSecurityConfiguration extends AbstractHttpConfigurer<CommonSecurityConfiguration, HttpSecurity> { @Override public void init(HttpSecurity http) throws Exception { // Your basic configuration here: // http.cors().and() // ... } public static CommonSecurityConfiguration commonSecurityConfiguration() { return new CommonSecurityConfiguration(); } } } 

    另请参阅文档中的示例。

  2. 注入Environment对象并检查当前配置文件:

     @Configuration @EnableWebSecurity(debug = true) @EnableGlobalMethodSecurity(prePostEnabled = true, jsr250Enabled = true, securedEnabled = true) public class SecurityConfiguration extends WebSecurityConfigurerAdapter { private final Environment environment; public SecurityConfiguration(Environment environment) { this.environment = environment; } @Profile("dev") @Bean public FilterRegistrationBean userIdAuthenticationFilter() { // ... } @Profile("!dev") @Bean public FilterRegistrationBean jwtAuthenticationFilter() { // ... } @Override protected void configure(HttpSecurity http) throws Exception { // Your basic configuration here: // http.cors().and() // ... if (environment.acceptsProfiles("dev")) { http.addFilter(userIdAuthenticationFilter().getFilter()); } else { http.addFilter(jwtAuthenticationFilter().getFilter()); } } } 

    或者,您可以使用应用程序属性而不是配置文件。

我有同样的需要添加一个过滤器来测试我的应用程序,它在请求中添加了一些标题。

@Autowired
private HeaderAddingFilter sudirHeaderAddingFilter;

 @Override
    protected void configure(HttpSecurity http) throws Exception {
     http.csrf().disable().

                    addFilterAt(new HeaderAuthenticationFilter(userDetailsService), UsernamePasswordAuthenticationFilter.class).
                    addFilterBefore(headerAddingFilter,HeaderAuthenticationFilter.class)
    .....

为了根据当前配置文件启用/禁用headerAddingFilter ,我声明了一个带有2个实现类的接口HeaderAddingFilter :第一个有@Profile("local") ,另一个有@Profile("!local") ,它什么也没做,只是继续doFilter()调用。

PS有意注册表示Eien的答案很棒且乐于助人。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM