我试图定义两个不同的豆(二者延伸AbstractPreAuthenticatedProcessingFilter):一个用于抓取一个头关闭请求(如USER_ID)的同时"显影"配置文件处于活动状态,第二个用于从请求标头中获取JWT,同时开发"个人资料不有效。 (从概念上讲,虽然,我' M真的只是试图基于豆本身的存在,以编程方式注册过滤器)目前,I' M甚至没有尝试使用配置文件,因为我' M有一个问题将标题自动注册到适当的过滤器链中。
该应用程序使用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(employeeNumberAuthenticationFilter())
.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
]
鉴于输出,它向我表明安全过滤器链不已应用我的过滤器。
也许更能说明问题的是,如果你' 11通知我的配置代码片断有注释掉两行(这是我在那里手动添加过滤器(一个或多个)之前,我试图让花哨的基于轮廓检测)。如果我取消注释添加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相比?有人可以指出我做错了什么以及为什么?
注册这些&#34;可选&#34;的正确方法是什么?豆子? (可选,因为可能不依赖于活动配置文件。)
谢谢!
答案 0 :(得分:5)
关于Spring Security架构的article:
Spring Security作为链中的单个Filter安装,其concerete类型是FilterChainProxy,原因很快就会显现出来。在Spring Boot应用程序中,安全过滤器是ApplicationContext中的@Bean,默认安装它以便它应用于每个请求。
可以有多个过滤器链,所有过滤器链都由Spring Security在同一顶级FilterChainProxy中管理,并且容器都是未知的。 Spring Security过滤器包含一个过滤器链列表,并将请求分派给匹配它的第一个链。
另请注意:
容器不知道Spring Security内部的所有过滤器都很重要,尤其是在Spring Boot应用程序中,默认情况下,所有类型为Filter的@Beans都会自动注册到容器中。因此,如果要向安全链添加自定义筛选器,则需要将其设置为@Bean或将其包装在显式禁用容器注册的FilterRegistrationBean中。
因此,当您将过滤器定义为Spring bean时,它会自动注册到servlet容器,但不会注册Spring Security过滤器链。这就是为什么你需要使用addFilter方法显式地将它添加到Spring Security链。您还需要在servlet容器中禁用自动注册,否则将调用过滤器两次。
另见:
至于配置文件,至少有两种方法可以满足您的需求:
扩展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();
}
}
}
另请参阅documentation。
注入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());
}
}
}
或者,您可以使用应用程序属性而不是配置文件。
答案 1 :(得分:0)
我同样需要添加一个用于测试我的应用的过滤器,该过滤器会在请求中添加一些标头。
@Autowired
private HeaderAddingFilter headerAddingFilter;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().
addFilterAt(new HeaderAuthenticationFilter(userDetailsService), UsernamePasswordAuthenticationFilter.class).
addFilterBefore(headerAddingFilter,HeaderAuthenticationFilter.class)
.....
为了根据当前配置文件启用/禁用headerAddingFilter
,我声明了具有两个实现类的接口HeaderAddingFilter
:第一个具有@Profile("local")
,另一个具有@Profile("!local")
,它什么也没做,只是继续进行doFilter()
调用。
P.S。故意注册以表明Eien的回答很好并且很有帮助。