本文讲一个spring security自定义filter非常容易出现的一个问题,那就是filter被执行两遍。
@EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Bean public DemoFilter demoFilter(){ return new DemoFilter(); } @Override protected void configure(HttpSecurity http) throws Exception { http .addFilterBefore(demoFilter(),AnonymousAuthenticationFilter.class) .authorizeRequests() .antMatchers("/login","/css/**", "/js/**","/fonts/**").permitAll() .anyRequest().authenticated(); } }
其中DemoFilter如下
public class DemoFilter extends GenericFilterBean { @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { //do something filterChain.doFilter(servletRequest, servletResponse); } }
在spring容器托管的GenericFilterBean的bean,都会自动加入到servlet的filter chain,而上面的定义,还额外把filter加入到了spring security的
AnonymousAuthenticationFilter之前。而spring security也是一系列的filter,在mvc的filter之前执行。因此在鉴权通过的情况下,就会先后各执行一次。
不把filter托管给spring,直接new,比如
@EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http .addFilterBefore(new DemoFilter(),AnonymousAuthenticationFilter.class) .authorizeRequests() .antMatchers("/login","/css/**", "/js/**","/fonts/**").permitAll() .anyRequest().authenticated(); } }
有时候filter需要访问spring容器的资源,托管给容器可能好些,那么这个时候,就可以像FilterSecurityInterceptor做个标记FILTER_APPLIED
public class DemoFilter extends GenericFilterBean { private static final String FILTER_APPLIED = "__spring_security_demoFilter_filterApplied"; @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { if (servletRequest.getAttribute(FILTER_APPLIED) != null) { filterChain.doFilter(servletRequest, servletResponse); return ; } //do something servletRequest.setAttribute(FILTER_APPLIED,true); filterChain.doFilter(servletRequest, servletResponse); } }