序
本文讲一个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之前执行。因此在鉴权通过的情况下,就会先后各执行一次。
解决方案
方案1
不把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();
}
}
方案2
有时候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);
}
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。