0

I am implementing a custom filter by extending UsernamePasswordAuthenticationFilter and overriding the requiresAuthentication method. However, in that method, I need to have access to the filter chain. Therefore I autowired it in. However, when trying to build, it fails saying that there is no such bean for the FilterChain.

web.xml:

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <!-- The definition of the Root Spring Container shared by all Servlets and Filters -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
            /WEB-INF/spring/security-context.xml
        </param-value>
    </context-param>

    <!-- Creates the Spring Container shared by all Servlets and Filters -->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <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>

</web-app>

security-context.xml:

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

    <security:http 
            realm="Protected API"
            use-expressions="true"
            auto-config="false"
            create-session="stateless"
            entry-point-ref="unauthorizedEntryPoint">
        <security:intercept-url pattern="/User/sign_up/*" access="permitAll" />
        <security:intercept-url pattern="/User/authenticate/**" access="permitAll" />
        <security:intercept-url pattern="/**" access="hasRole('ROLE_USER')" />
        <security:custom-filter ref="authenticationTokenFilter" position="FORM_LOGIN_FILTER" />
    </security:http>

    <authentication-manager alias="authenticationManager">
        <authentication-provider ref="customAuthenticationProvider" />
    </authentication-manager>

    <beans:bean id="customAuthenticationProvider" class="pathTo:CustomAuthenticationProvider"></beans:bean>

    <beans:bean id="unauthorizedEntryPoint" class="pathTo:UnauthorizedEntryPoint" />

     <beans:bean id="mySuccessHandler" class="pathTo:SuccessHandler" />

    <beans:bean
        class="pathTo:AuthenticationTokenFilter"
        id="authenticationTokenFilter">
        <beans:property name="authenticationManager" ref="authenticationManager" />
        <beans:property name="postOnly" value="false" />
        <beans:property name="authenticationSuccessHandler" ref="mySuccessHandler" />
    </beans:bean>


    <context:component-scan base-package="base package" />
    <context:annotation-config />


</beans:beans>

Filter:

public class AuthenticationTokenFilter extends UsernamePasswordAuthenticationFilter  {  

    @Autowired
    FilterChain chain;


    @Override
    protected boolean requiresAuthentication(HttpServletRequest request,
            HttpServletResponse response) {
        System.out.println("here");
        boolean retVal = false;
        String username = request.getHeader("j_username");
        String password = request.getHeader("j_password");
        System.out.println("username: " + username + " password: " + password);
        if (username != null && password != null) {
            Authentication authResult = null;
            try {
                authResult = attemptAuthentication(request, response);
                System.out.println(authResult.getName());
                if (authResult == null) {
                    retVal = false;
                }
            } catch (AuthenticationException failed) {
                try {
                    System.out.println("auth failed");
                    unsuccessfulAuthentication(request, response, failed);
                } catch (IOException e) {
                    retVal = false;
                } catch (ServletException e) {
                    retVal = false;
                }
                retVal = false;
            }
            try {
                successfulAuthentication(request, response, chain, authResult);
            } catch (IOException e) {
                retVal = false;
            } catch (ServletException e) {
                retVal = false;
            }
            return false;
        } else {
            System.out.println("not calling authenticate");
            retVal = true;
        }
        return retVal;
    }

    protected String obtainPassword(HttpServletRequest request) {
        return request.getHeader("j_password");
    }

    protected String obtainUsername(HttpServletRequest request) {
        return request.getHeader("j_username");
    }

Is there another way to do this without the filterChain? Otherwise, it should be able to do this since filterChain is defined in web.xml.

Any Thoughts on how to solve this?

zyork
  • 65
  • 2
  • 11

2 Answers2

0

Changing the code might work

<beans:bean
    class="pathTo:AuthenticationManagerHandler"
    id="authenticationTokenFilter">
    <beans:property name="authenticationManager" ref="authenticationManager" />
    <beans:property name="postOnly" value="false" />
    <beans:property name="authenticationSuccessHandler" ref="mySuccessHandler" />
</beans:bean>

And here is the AuthenticationManagerHandler class

public class AuthenticationManagerHandler extends AbstractAuthenticationProcessingFilter {

private static final String INTERCEPTOR_PROCESS_URL = "/j_spring_security_check";

private AuthenticationUtil util;

public AuthenticationManagerHandler() {
    super(INTERCEPTOR_PROCESS_URL);        
}

@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {

    String username = request.getParameter("j_username");
    String password = request.getParameter("j_password");
    UsernamePasswordAuthenticationToken authRequest = null;
    if(util== null) {
        util= ApplicationContext.getInstance(AuthenticationUtil.class);
    }
    if(util.isLDAPEnalbed()) {
        authRequest = new LDAPUsernamePasswordAuthenticationToken(username, password);            
    }
    else {
        authRequest = new DBUsernamePasswordAuthenticationToken(username, password);    
    }
    setDetails(request, authRequest);
    return getAuthenticationManager().authenticate(authRequest);
}

protected void setDetails(HttpServletRequest request, UsernamePasswordAuthenticationToken authRequest)
{
  authRequest.setDetails(this.authenticationDetailsSource.buildDetails(request));
}

private static final class SubStringFilterProcessUrlRequestMatcher implements RequestMatcher {

    private final List<String> filterProcessUrls;

    private SubStringFilterProcessUrlRequestMatcher(List<String> filterProcessUrls) {
        Assert.notEmpty(filterProcessUrls, "filterProcessesUrl must be specified");
        for (String filterProcessesUrl : filterProcessUrls) {
            Assert.isTrue(UrlUtils.isValidRedirectUrl(filterProcessesUrl), filterProcessesUrl + " isn't a valid redirect URL");
        }
        this.filterProcessUrls = filterProcessUrls;
    }

    public boolean matches(HttpServletRequest request) {
        for (String filterProcessesUrl : filterProcessUrls) {
            if (matches(request, filterProcessesUrl)) {
                return true;
            }

        }
        return false;
    }

    private boolean matches(HttpServletRequest request, String filterProcessesUrl) {
        String uri = request.getRequestURI();
        int pathParamIndex = uri.indexOf(';');

        if (pathParamIndex > 0) {
            // strip everything after the first semi-colon
            uri = uri.substring(0, pathParamIndex);
        }

        if ("".equals(request.getContextPath())) {
            return uri.endsWith(filterProcessesUrl);
        }

        return uri.contains(filterProcessesUrl);
    }
}    

}
Swathi
  • 602
  • 4
  • 17
0

I ended up changing my Filter implementation to just call my authentication provider directly (How I should have been doing it to begin with. I also removed the success handler because it ended up not being what I wanted to implement. I will share my updated Filter below:

public class AuthenticationTokenFilter extends UsernamePasswordAuthenticationFilter  {  

    @Autowired
    private CustomAuthenticationProvider authProvider;


    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {

        String username = obtainUsername(request);
        String password = obtainPassword(request);

        if (username == null) {
            username = "";
        }

        if (password == null) {
            password = "";
        }

        username = username.trim();

        UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);

        // Allow subclasses to set the "details" property
        setDetails(request, authRequest);

        return this.authProvider.authenticate(authRequest);
    }



    @Override
    protected String obtainPassword(HttpServletRequest request) {
        return request.getHeader("X_password");
    }

    @Override
    protected String obtainUsername(HttpServletRequest request) {
        return request.getHeader("X_username");
    }

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

        HttpServletRequest httpRequest = (HttpServletRequest) request;
        HttpServletResponse httpResponse = (HttpServletResponse) response;

        Authentication auth = attemptAuthentication(httpRequest, httpResponse);
        SecurityContextHolder.getContext().setAuthentication(auth);

        chain.doFilter(request, response);

    }



}

This avoids the issue of having to call successfulAuthentication (which my other approach needed). This approach is simpler now that successfulAuthentication without the FilterChain is deprecated.

Thanks for the help!

zyork
  • 65
  • 2
  • 11