定制弹簧过滤器导致链条中的下一个过滤器不能点火?

问题描述 投票:2回答:4

我正在使用一个自定义的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一样。

这是我的spring security.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;
    }
}

总结:我制作了一个自定义安全过滤器,它似乎不会调用其后列出的任何过滤器。如果我删除我的自定义过滤器并使用内置的安全性:http-basic,它可以正常工作。

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

java spring spring-mvc spring-security servlet-filters
4个回答
2
投票

你想尝试添加吗?

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

    chain.doFilter(req, res);
}

自定义令牌验证过滤器类?


2
投票

据我所知,Spring身份验证过滤器用于验证请求,然后重定向到某个地方,启动一个全新的请求。这意味着有目的地停止过滤器链。这个想法是,一旦经过身份验证并且在新请求中,将存在身份验证会话,因此身份验证筛选器不必再次重定向。至少,这是我从一点点调试的理解。

为了解决此问题,您必须实现自己的AbstractAuthenticationProcessingFilter,AuthenticationEntryPoint,AbstractAuthenticationToken,AuthenticationProvider和SimpleUrlAuthenticationSuccessHandler。


1
投票

克里斯托弗在回答中提到的是正确的,但我认为展示一些代码会更好。对于我的情况,我通过扩展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处停止。然后,我找到了克里斯托弗的答复并按照他所分享的方式行事,这是:

将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上进行探索。然后,您应该能够看到从日志中触发所有过滤器链。

希望能帮助到你 :)


0
投票

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;
}
© www.soinside.com 2019 - 2024. All rights reserved.