我正在尝试定义两个不同的bean(都扩展了AbstractPreAuthenticatedProcessingFilter):一个用于在“开发”配置文件处于活动状态时从请求(例如USER_ID)中获取标头,第二个在请求时从请求标头中获取JWT。 “开发”资料无效 。(尽管从概念上讲,我实际上只是在尝试基于bean本身的存在来以编程方式注册过滤器)目前,我什至没有尝试使用配置文件,因为我遇到了自动将标头自动设置的问题在相应的过滤器链中注册。
该应用程序使用配置为使用嵌入式Tomcat的Spring-Boot 2.0.0.RELEASE,并将该服务注释为@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 dump了以下输出(调试打开):
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之间有什么区别?正常的过滤器链,相对于在弹簧安全性过滤器链中注册的豆子呢?有人可以指出我做错了什么,为什么?
注册这些“可选” bean的正确方法是什么?(可选,因为可能不依赖于活动概要文件。)
谢谢!
从有关Spring Security体系结构的文章中:
Spring Security是作为链中的单个Filter安装的,其隐秘类型为FilterChainProxy,原因很快就会变得显而易见。在Spring Boot应用程序中,安全过滤器是ApplicationContext中的@Bean,默认情况下会安装该过滤器,以便将其应用于每个请求。
在同一顶级FilterChainProxy中可以有多个由Spring Security管理的过滤器链,而对于容器来说都是未知的。Spring Security过滤器包含一个过滤器链列表,并向与其匹配的第一个链调度一个请求。
另请注意:
容器不知道Spring Security内部的所有过滤器这一事实很重要,尤其是在Spring Boot应用程序中,默认情况下,所有Filter类型的@Bean都会自动向容器注册。因此,如果要向安全链中添加自定义过滤器,则无需将其设置为@Bean或将其包装在明确禁用容器注册的FilterRegistrationBean中。
因此,当您将过滤器定义为Spring bean时,它会自动向servlet容器注册,而不是向Spring Security过滤器链注册。这就是为什么您需要使用addFilter方法将其显式添加到Spring Security链中的原因。您还需要在servlet容器中禁用自动注册,否则过滤器将被调用两次。
也可以看看:
75.3将Servlet,过滤器或侦听器添加到应用程序
将@Component添加到自定义Spring Security过滤器意味着什么
至于配置文件,至少有两种方法可以满足您的需求:
扩展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{ @Override public void init(HttpSecurity http) throws Exception { // Your basic configuration here: // http.cors().and() // ... } public static CommonSecurityConfiguration commonSecurityConfiguration() { return new CommonSecurityConfiguration(); } } }
另请参见文档中的示例。
注入环境对象并检查当前配置文件:
@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()); } } }
或者,您可以为此使用应用程序属性而不是配置文件。