安全过滤器重叠

时间:2016-01-30 04:22:14

标签: spring spring-security spring-boot spring-oauth2

当多个过滤器添加到HttpSecurity配置方法时,它们似乎重叠,因为当时只有一个有效。

这是配置方法:

@Override
public void configure(HttpSecurity http) throws Exception {
    http
            .logout().and().antMatcher("/**")
            .addFilterBefore(ssoFilter(), RequestHeaderAuthenticationFilter.class)
            .authenticationProvider(preauthAuthProvider())
            .authorizeRequests()
            .antMatchers("/index.html", "/home.html", "/", "/login").permitAll()
            .anyRequest().authenticated().and().csrf()
            .csrfTokenRepository(csrfTokenRepository()).and()
            .addFilterAfter(csrfHeaderFilter(), CsrfFilter.class);
}

我已尝试指定订单,但问题仍然存在:

@Bean
public FilterRegistrationBean securityFilterChain(@Qualifier(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME) Filter securityFilter) {
    FilterRegistrationBean registration = new FilterRegistrationBean(securityFilter);
    registration.setOrder(Integer.MAX_VALUE - 2);
    registration.setName(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME);
    return registration;
}

@Bean
public FilterRegistrationBean ssoFilterRegistrationBean() throws Exception {
    FilterRegistrationBean registrationBean = new FilterRegistrationBean();
    registrationBean.setFilter(ssoFilter());
    registrationBean.setOrder(Integer.MAX_VALUE-1);
    return registrationBean;
}

@Bean
public FilterRegistrationBean csrfFilterRegistrationBean() throws Exception {
    FilterRegistrationBean registrationBean = new FilterRegistrationBean();
    registrationBean.setFilter(csrfHeaderFilter());
    registrationBean.setOrder(Integer.MAX_VALUE);
    return registrationBean;
}

我跟着以下帖子没有成功。

Filter order in spring-boot

https://github.com/spring-projects/spring-boot/issues/1640

https://github.com/spring-projects/spring-boot/issues/677

任何帮助将不胜感激!

更新:

CSRF过滤器定义

private Filter csrfHeaderFilter() {
    return new OncePerRequestFilter() {
        @Override
        protected void doFilterInternal(HttpServletRequest request,
                                        HttpServletResponse response, FilterChain filterChain)
                throws ServletException, IOException {
            CsrfToken csrf = (CsrfToken) request
                    .getAttribute(CsrfToken.class.getName());
            if (csrf != null) {
                Cookie cookie = WebUtils.getCookie(request, "XSRF-TOKEN");
                String token = csrf.getToken();
                if (cookie == null
                        || token != null && !token.equals(cookie.getValue())) {
                    cookie = new Cookie("XSRF-TOKEN", token);
                    cookie.setPath("/");
                    response.addCookie(cookie);
                }
            }
            filterChain.doFilter(request, response);
        }
    };
}

SSO过滤器定义:

public class SSORequestHeaderAuthenticationFilter extends RequestHeaderAuthenticationFilter {

private boolean allowPreAuthenticatedPrincipals = true;

public SSORequestHeaderAuthenticationFilter() {
    super();
    //TODO Pull this value from a properties file (application.properties, or localstrings.properties)
    //NOTE SM_USER is the default, but you can change it like this (your company may use some other header)
    //this.setPrincipalRequestHeader("SM_USER");
}


@Override
public void doFilter(ServletRequest request, ServletResponse response,
                     FilterChain chain) throws IOException, ServletException {
    chain.doFilter(request, response);
}

/**
 * This is called when a request is made, the returned object identifies the
 * user and will either be {@literal null} or a String. This method will throw an exception if
 * exceptionIfHeaderMissing is set to true (default) and the required header is missing.
 *
 * @param request {@link javax.servlet.http.HttpServletRequest}
 */
@Override
protected Object getPreAuthenticatedPrincipal(HttpServletRequest request) {
    String userName = (String) (super.getPreAuthenticatedPrincipal(request));
    if (userName == null || userName.trim().equals("")) {
        return userName;
    }

    return userName;
}

public boolean isAllowPreAuthenticatedPrincipals() {
    return allowPreAuthenticatedPrincipals;
}
}

3 个答案:

答案 0 :(得分:0)

我的猜测是你并不总是在两个过滤器中执行FilterChain.doFilter方法。然后过滤器链停止,只执行一个自定义过滤器。在这个简单的例子中,两个过滤器都执行了:

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .addFilterBefore(new Filter1(), RequestHeaderAuthenticationFilter.class)
                .addFilterAfter(new Filter2(), CsrfFilter.class)
                .authorizeRequests()
                .anyRequest().authenticated()
                .and()
                .httpBasic()
                .and()
                .logout()
                .permitAll();
    }

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth
                .inMemoryAuthentication()
                .withUser("user").password("password").roles("USER");
    }

    static class Filter1 extends OncePerRequestFilter {
        @Override
        protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
            System.out.println("executed filter 1");
            filterChain.doFilter(request, response);
        }
    }

    static class Filter2 extends OncePerRequestFilter {
        @Override
        protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
            System.out.println("executed filter 2");
            filterChain.doFilter(request, response);
        }
    }

}

答案 1 :(得分:0)

您正在混淆容器注册(使用HttpSecurity)并在安全过滤器链中注册(使用FilterRegistrationBeans),也可能使用Spring Security中的过滤器链的顺序。如果Spring Security选择了给定的过滤器链,那么它中的所有过滤器甚至都不需要被激活(过滤器总是可以关闭其他下游过滤器)。

我建议您不要担心@Order中的订单,并使用它们来禁用容器注册(通过将其启用标志设置为false)。然后考虑WebSecurityConfigurersaddFilter{Before,After}指定的过滤器链的顺序。最后,您可以决定给定链中过滤器的顺序是否重要,以及它是否使用{{1}}方法。

答案 2 :(得分:0)

执行您要执行的操作的过滤器已存在。它是OAuth2AuthenticationProcessingFilter。 如果使用@EnableResourceServer注释应用程序,则使用此过滤器。如果这样做,这将导致仅基于令牌的身份验证现在可以正常工作。 您必须将此过滤器的无状态标志设置为false,以允许其他身份验证方式。

我所做的是创建一个扩展ApiTokenAccessFilter的班级OAuth2AuthenticationProcessingFilter。此过滤器采用ResourceServerTokenServices构造函数参数,并将无状态标志设置为false。

public class ApiTokenAccessFilter extends OAuth2AuthenticationProcessingFilter {

  public ApiTokenAccessFilter(ResourceServerTokenServices resourceServerTokenServices) {

    super();
    setStateless(false);
    setAuthenticationManager(oauthAuthenticationManager(resourceServerTokenServices));
  }

  private AuthenticationManager oauthAuthenticationManager(ResourceServerTokenServices tokenServices) {

    OAuth2AuthenticationManager oauthAuthenticationManager = new OAuth2AuthenticationManager();

    oauthAuthenticationManager.setResourceId("oauth2-resource");
    oauthAuthenticationManager.setTokenServices(tokenServices);
    oauthAuthenticationManager.setClientDetailsService(null);

    return oauthAuthenticationManager;
  }
}

在我的安全配置中,我使用此过滤器如下:

@Configuration
@EnableOAuth2Sso
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

  @Autowired
  private ResourceServerTokenServices tokenServices;

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

    http.authorizeRequests()
        .anyRequest()
        .authenticated()
        .and()
        .addFilterBefore(new ApiTokenAccessFilter(tokenServices), AbstractPreAuthenticatedProcessingFilter.class);
  }
}

我认为这可能会更容易,所以我在spring-security-oauth Github repo上打开了一个问题。我不确定这种解决方案是否可行,但我没有找到另一种选择。