【问题标题】:Security filters are overlapping安全过滤器重叠
【发布时间】:2016-05-07 23:31:47
【问题描述】:

当向 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;
}
}

【问题讨论】:

  • 可以添加过滤代码吗?
  • 只是一个问题。您是否正在使用 OAuth2 和 @EnableOauth2Sso 并尝试对授权标头中包含不记名令牌的请求进行身份验证?几天前我已经这样做了。如果这是您想要实现的目标,我可以为您提供解决方案
  • @YannicKlem 天哪,是的!!!!这正是我的情况,这个应用程序有 @ EnableOaut2Sso 注释,它保留令牌作为重定向发送到另一个应用程序,这就是我有 csrf 过滤器的原因:)谢谢这真的很有帮助!
  • 希望你能等到星期三。我现在无法访问代码,我想在提供此解决方案之前尝试一些东西。
  • 我提供了我的解决方案。如果您找到了其他解决方案,请告诉我。

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


【解决方案1】:

我的猜测是你并不总是在两个过滤器中执行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);
        }
    }

}

【讨论】:

  • 试过了,没用 :(。我会用过滤器定义更新问题。
【解决方案2】:

您混淆了容器注册(使用FilterRegistrationBean)和安全过滤器链中的注册(使用HttpSecurity),还可能混淆了Spring Security 中过滤器链的顺序。如果 Spring Security 选择了给定的过滤器链,则其中的所有过滤器都不一定会被触发(过滤器总是可以关闭其他下游过滤器)。

我建议你不要再担心FilterRegistrationBeans 中的顺序,并使用它们来禁用容器注册(通过将它们的启用标志设置为 false)。然后想想你的过滤器链的顺序,正如你的WebSecurityConfigurers 上的@Order 所指定的那样。最后,您可以决定给定链中过滤器的顺序是否重要,以及是否使用addFilter{Before,After} 方法。

【讨论】:

  • 在尝试使用 FilterRegistrationBean 之前,我使用的是 addFilterBefore 和 addFilterAfter 但即使我只使用了 ssoFilter 工作,甚至没有调用 csrf。
  • 再次阅读答案。如果您的自定义过滤器被声明为 bean,那么您需要禁用容器注册。过滤器未触发并不意味着它未注册。
  • 我按照你说的做了,让 csrf 过滤器工作了。问题是,如果有人以前经历过这种情况,我环顾四周,我使用 ssoFilter() 的 preAuthProvider * 完全更改了主体对象,因此丢失了与现在已成为问题的 csrfFilter 相关的值。有没有办法在申请授权过程中只更改 Principal 对象中的权限列表?
  • 这是一个不同的问题?最好不要继续在 cmets 中。
【解决方案3】:

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

我所做的是创建一个扩展OAuth2AuthenticationProcessingFilter 的类ApiTokenAccessFilter。此过滤器采用 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 上打开了一个问题。我不确定这个解决方案是否可行,但我没有找到另一种选择。

【讨论】:

    猜你喜欢
    • 2015-04-01
    • 2016-05-07
    • 1970-01-01
    • 2013-10-05
    • 2017-03-18
    • 2017-03-18
    相关资源
    最近更新 更多