【问题标题】:Custom Spring filter causing next filter in chain not to fire?自定义 Spring 过滤器导致链中的下一个过滤器不触发?
【发布时间】:2015-05-08 15:58:15
【问题描述】:

我使用了一个自定义 Spring Security 过滤器,它覆盖了 AbstractAuthenticationProcessingFilter 但我一定写错了,因为它似乎永远不会调用过滤器链的其余部分。具体来说,我依靠 OpenEntityManagerInViewFilter 过滤器来确保 Jackson+Hibernate 可以处理延迟加载的对象。

如果我的 web.xml 首先有 OpenEntityManagerInViewFilter,一切正常:

<filter>
    <filter-name>hibernateFilterChain</filter-name>
    <filter-class>org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>hibernateFilterChain</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

<filter>
    <filter-name>springSecurityFilterChain</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>

<filter-mapping>
    <filter-name>springSecurityFilterChain</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

但是,如果我将 springSecurityFilterChain 放在顶部,我的应用程序的行为就好像我根本没有指定 OpenEntityManagerInViewFilter。

这是我的 springSecurity.xml:

<?xml version="1.0"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:security="http://www.springframework.org/schema/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
    http://www.springframework.org/schema/security
    http://www.springframework.org/schema/security/spring-security-3.2.xsd">

<security:http entry-point-ref="restAuthenticationEntryPoint"
    use-expressions="true" create-session="stateless">

    <security:custom-filter ref="authenticationTokenProcessingFilter"
        position="FORM_LOGIN_FILTER" />
    <security:intercept-url pattern="/**"
        access="isAuthenticated()" />

    <security:logout />
</security:http>

<bean class="edu.ucdavis.dss.dw.security.CustomTokenAuthenticationFilter"
    id="authenticationTokenProcessingFilter">
    <constructor-arg type="java.lang.String">
        <value>/**</value>
    </constructor-arg>
</bean>

<security:authentication-manager>
    <security:authentication-provider
        user-service-ref="userService"></security:authentication-provider>
</security:authentication-manager>

<bean id="userService" class="edu.ucdavis.dss.dw.services.UserAuthenticationService"></bean>

</beans>

最后,这里是 CustomTokenAuthenticationFilter 本身,这可能会导致问题:

public class CustomTokenAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
    @Autowired @Qualifier("org.springframework.security.authenticationManager")
    private AuthenticationManager authenticationManager;

    public CustomTokenAuthenticationFilter(String defaultFilterProcessesUrl) {
        super(defaultFilterProcessesUrl);
        super.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher(defaultFilterProcessesUrl));
        setAuthenticationManager(new NoOpAuthenticationManager());
        setAuthenticationSuccessHandler(new TokenSimpleUrlAuthenticationSuccessHandler());
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
        String token = request.getParameter("token");

        if(token == null) {
            throw new AuthenticationServiceException("Token Missing");
        }

        Authentication authResponse;

        try {
            authResponse = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(token, "dssit"));
        } catch (AuthenticationException e) {
            throw new AuthenticationServiceException("Bad Token");
        }

        return authResponse;
    }
}

总而言之:我制作了一个自定义安全过滤器,它似乎没有调用它后面列出的任何过滤器。如果我删除我的自定义过滤器并使用一些内置的东西,比如 security:http-basic,它就可以正常工作。

提前感谢您提供的任何帮助。

【问题讨论】:

    标签: java spring spring-mvc spring-security servlet-filters


    【解决方案1】:

    要不要尝试添加

    public void doFilter(javax.servlet.ServletRequest req,
            javax.servlet.ServletResponse res,
            javax.servlet.FilterChain chain)
              throws IOException,
                     javax.servlet.ServletException {
    
        chain.doFilter(req, res);
    }
    

    到 CustomTokenAuthenticationFilter 类?

    【讨论】:

    • 我认为这不是一个好的提示。这样的方法已经在超类中,并且可以使用更多的逻辑。我们需要这个逻辑,并且不想覆盖它。
    • @Géza,OP 提到过滤器没有通过到下一个级别,我希望覆盖该方法可以解决这个问题。然后他需要自己实现其他细节。
    • 你们都或多或少是正确的。超类确实处理了这个问题,但它是身份验证处理过滤器的设计,它阻止了我认为的下一个过滤器。看我的回答。
    【解决方案2】:

    据我所知,Spring 身份验证过滤器旨在对请求进行身份验证,然后重定向到某个地方,开始一个全新的请求。这意味着过滤器链被故意停止。这个想法是,一旦通过身份验证并在新请求中,身份验证会话将已经存在,因此身份验证过滤器不必再次重定向。至少,这是我通过一点调试得到的理解。

    为了避免这种行为,您必须实现自己的 AbstractAuthenticationProcessingFilter、AuthenticationEntryPoint、AbstractAuthenticationToken、AuthenticationProvider 和 SimpleUrlAuthenticationSuccessHandler。

    【讨论】:

      【解决方案3】:

      AbstractAuthenticationProcessingFilter 类具有以下可以被覆盖的方法:

      protected boolean requiresAuthentication(HttpServletRequest request,
              HttpServletResponse response)
      

      在使用过滤器(在doFilter)启动身份验证之前调用此方法:

      if (!requiresAuthentication(request, response)) {
          chain.doFilter(request, response);
      
          return;
      }
      

      这个方法应该被子类覆盖,以检查它是否可以验证请求:如果令牌不存在或无效,它应该返回 false。

      例如:

      @Override
      protected boolean requiresAuthentication(HttpServletRequest request, HttpServletResponse response) {
      
          if (!super.requiresAuthentication(request, response)) {
             // We're not required to authenticate this request (ant matchers).
             return false;
          }
      
          if (null == request.getParameter("token")) {
             // We can't authenticate this request, because the token is missing.
             return false;
          }
      
          try {
            authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(token, "dssit"));
          } catch (AuthenticationException e) {
            // We can't authenticate this token because an exception occurred.
            return false;
          }
      
          // We can authenticate this request.
          return true;
      }
      

      【讨论】:

      • 谢谢,这个答案对我来说确实帮助解决了这个问题
      【解决方案4】:

      Christopher 在他的回复中提到的是正确的,但我认为显示一些代码会更好。就我而言,我通过扩展 UsernamePasswordAuthenticationFilter 和自定义 AuthenticationSuccessHandler 实现来实现自定义身份验证方法。

      一开始,我有如下实现,用于 authenticationSuccessHandler 返回响应

      httpServletResponse.setStatus(HttpServletResponse.
      httpServletResponse.setContentType("application/json");
      httpServletResponse.getWriter().write(loginSuccessResponse);
      httpServletResponse.getWriter().flush();
      

      但是,当我使用以下命令打开调试时:

      @EnableWebSecurity(debug = true)
      

      并为 org.springframework.security.web.FilterChainProxy 启用调试日志记录。

      我意识到安全过滤器链在自定义实现 UsernamePasswordAuthenticationFilter 处停止。然后,我找到了Christopher的回复,按照他分享的做了,就是:

      将 AuthenticationSuccessHandler 中的响应重定向到另一个端点:

      public class CustomAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler
          implements AuthenticationSuccessHandler  {
      
          @Override
          public void onAuthenticationSuccess(HttpServletRequest httpServletRequest,
                                          HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
      
              httpServletResponse.sendRedirect(ApiResourceConstant.POST_LOGIN);
          }
      }
      

      您也可以在 SimpleUrlAuthenticationSuccessHandler 上进行探索。然后,您应该能够看到从日志中触发的所有过滤器链。

      希望对你有帮助:)

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2015-06-27
        • 2020-11-04
        • 2019-12-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-08-23
        相关资源
        最近更新 更多