Spring Security : Bypass login form

Dchucks picture Dchucks · Sep 30, 2010 · Viewed 18.4k times · Source

I want to bypass the login form for a Spring webflow (Spring 2.0.5) application under certain scenarios (so the login form is presented for normal users but when the URL is like http://server.com/myspringapp/fakelogin?username=FakeUser&password=FakePassword then the user should not be presented the login form but just authenticated internally based on request parameters and then taken to the secure page).

So I don't want Preauthenticastion, rather a transparent authentication on special occassions (when the URL is as mentioned above). I saw threads such as http://forum.springsource.org/showthread.php?t=59108 but no where a solution is mentioned. I tried implementing the AuthenticationProcessingFilter but not too sure how to implement the requiresAuthentication() method.

Following is my current security XML:

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

    <bean id="customAuthenticationProvider" class="com.myco.jsf.spring.security.MyAuthenticationProvider">
        <security:custom-authentication-provider/>
        <constructor-arg>
            <ref bean="webSessionFactory"/>
        </constructor-arg>
        <constructor-arg>
            <ref bean="authenticationBridge"/>
        </constructor-arg>
    </bean>

    <bean id="myEntryPoint" class="com.myco.web.filter.CustomAuthenticationEntryPoint">
        <property name="loginFormUrl" value="/spring/login" />
    </bean>

    <bean id="myProcessingFilter" class="com.myco.web.filter.CustomAuthenticationProcessingFilter">
        <security:custom-filter position="AUTHENTICATION_PROCESSING_FILTER" />
        <property name="defaultTargetUrl" value="/spring/secure" /> 
        <property name="authenticationFailureUrl" value="/spring/login" />
        <property name="alwaysUseDefaultTargetUrl" value="false" />
        <property name="filterProcessesUrl" value="/spring/j_spring_security_check" />    
        <property name="authenticationManager" ref="authenticationManager" />            
        <!--             
        <property name="allowSessionCreation" value="true" />                
         -->
    </bean>    

    <security:authentication-manager alias="authenticationManager"/>

        <security:http auto-config="false" access-denied-page="/spring/notpermitted" entry-point-ref="myEntryPoint">       
        <security:anonymous/>
        <!--     
        <security:form-login login-page="/spring/login" login-processing-url="/spring/j_spring_security_check" default-target-url="/spring/secure" 
            always-use-default-target="false" authentication-failure-url="/spring/login" />
         -->
        <security:logout logout-url="/spring/j_spring_security_logout" logout-success-url="/spring/pages/logout" />
        </security:http>

</beans> 

Following is my filter class:

public class CustomAuthenticationProcessingFilter extends
        AuthenticationProcessingFilter {

    @Override
    protected void successfulAuthentication(HttpServletRequest request,
            HttpServletResponse response, Authentication authResult)
            throws IOException, ServletException {
        super.successfulAuthentication(request, response, authResult);

        System.out.println("==successful login==");
    }

    @Override
    protected void unsuccessfulAuthentication(HttpServletRequest request,
            HttpServletResponse response, AuthenticationException failed)
            throws IOException, ServletException {
        super.unsuccessfulAuthentication(request, response, failed);

        System.out.println("==failed login==");
    }

    @Override
    protected boolean requiresAuthentication(HttpServletRequest request,
            HttpServletResponse response) {
        boolean retVal = false;
        String username = request.getParameter("j_username");
        String password = request.getParameter("j_password");

        if (username != null && password != null) {
            Authentication authResult = null;
            try {
                authResult = attemptAuthentication(request);                
                if (authResult == null) {
                    retVal = false;
                }

            } catch (AuthenticationException failed) {
                try {
                    unsuccessfulAuthentication(request, response, failed);
                } catch (Exception e) {
                    retVal = false;
                }
                retVal = false;
            }

            try {
                successfulAuthentication(request, response, authResult);
            } catch (Exception e) {
                retVal = false;
            }

            return false;
        } else {
            retVal = super.requiresAuthentication(request, response);
        }
        return retVal;
    }

}

I am able to authenticate using the request parameters supplied and the Authentication object is created successfully. Its once the filter proceeds forward that I get the exception:

15:29:08,734 INFO  [STDOUT] 53453 ERROR [http-127.0.0.1-8080-2]     org.ajax4jsf.webapp.BaseXMLFilter     - Exception in the filter chain
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is java.lang.IllegalStateException
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:659)
    at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:552)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:617)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.ajax4jsf.webapp.BaseXMLFilter.doXmlFilter(BaseXMLFilter.java:206)
    at org.ajax4jsf.webapp.BaseFilter.handleRequest(BaseFilter.java:290)
    at org.ajax4jsf.webapp.BaseFilter.processUploadsAndHandleRequest(BaseFilter.java:388)
    at org.ajax4jsf.webapp.BaseFilter.doFilter(BaseFilter.java:515)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at com.myco.jsf.filter.CharsetFilter.doFilter(CharsetFilter.java:38)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:378)
    at org.springframework.security.intercept.web.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:109)
    at org.springframework.security.intercept.web.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:83)
    at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
    at org.springframework.security.ui.SessionFixationProtectionFilter.doFilterHttp(SessionFixationProtectionFilter.java:67)
    at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
    at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
    at org.springframework.security.ui.ExceptionTranslationFilter.doFilterHttp(ExceptionTranslationFilter.java:101)
    at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
    at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
    at org.springframework.security.providers.anonymous.AnonymousProcessingFilter.doFilterHttp(AnonymousProcessingFilter.java:105)
    at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
    at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
    at org.springframework.security.wrapper.SecurityContextHolderAwareRequestFilter.doFilterHttp(SecurityContextHolderAwareRequestFilter.java:91)
    at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
    at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
    at org.springframework.security.ui.AbstractProcessingFilter.doFilterHttp(AbstractProcessingFilter.java:278)
    at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
    at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
    at org.springframework.security.ui.logout.LogoutFilter.doFilterHttp(LogoutFilter.java:89)
    at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
    at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
    at org.springframework.security.context.HttpSessionContextIntegrationFilter.doFilterHttp(HttpSessionContextIntegrationFilter.java:235)
    at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
    at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
    at org.springframework.security.util.FilterChainProxy.doFilter(FilterChainProxy.java:175)
    at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:237)
    at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:167)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.jboss.web.tomcat.filters.ReplyHeaderFilter.doFilter(ReplyHeaderFilter.java:96)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:230)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:173)
    at org.jboss.web.tomcat.security.SecurityAssociationValve.invoke(SecurityAssociationValve.java:182)
    at org.jboss.web.tomcat.security.JaccContextValve.invoke(JaccContextValve.java:84)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:128)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:104)
    at org.jboss.web.tomcat.service.jca.CachedConnectionValve.invoke(CachedConnectionValve.java:157)
    at org.apache.catalina.authenticator.SingleSignOn.invoke(SingleSignOn.java:393)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:241)
    at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:844)
    at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:583)
    at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:447)
    at java.lang.Thread.run(Thread.java:619)
Caused by: java.lang.IllegalStateException
    at org.apache.catalina.connector.ResponseFacade.sendError(ResponseFacade.java:407)
    at javax.servlet.http.HttpServletResponseWrapper.sendError(HttpServletResponseWrapper.java:108)
    at org.springframework.security.context.HttpSessionContextIntegrationFilter$OnRedirectUpdateSessionResponseWrapper.sendError(HttpSessionContextIntegrationFilter.java:498)
    at javax.servlet.http.HttpServletResponseWrapper.sendError(HttpServletResponseWrapper.java:108)
    at org.ajax4jsf.webapp.FilterServletResponseWrapper.sendError(FilterServletResponseWrapper.java:655)
    at com.sun.facelets.FaceletViewHandler.handleFaceletNotFound(FaceletViewHandler.java:711)
    at com.sun.facelets.FaceletViewHandler.renderView(FaceletViewHandler.java:658)
    at org.ajax4jsf.application.ViewHandlerWrapper.renderView(ViewHandlerWrapper.java:100)
    at org.ajax4jsf.application.AjaxViewHandler.renderView(AjaxViewHandler.java:176)
    at org.springframework.faces.mvc.JsfView.renderMergedOutputModel(JsfView.java:83)
    at org.springframework.web.servlet.view.AbstractView.render(AbstractView.java:250)
    at org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java:1060)
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:798)
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:716)
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:647)
    ... 61 more

Can you help me with why I am getting this error? Am I using the correct type of custom filter? Appreciate your help.

Answer

martin picture martin · Feb 4, 2011

I did something similar with Spring Security 3 and I think it should be possible with older versions too. I've modified my code, so it fits your situation. You might need to work out some of the details, but it should provide you with the basic idea.

You can handle it using a filter:

public class MyAuthenticationFilter extends DelegatingFilterProxy
{
    public void doFilter ...
    {
            String username = request.getParameter("username");
            String password = request.getParameter("password");

            // build authentication token for user
            final Authentication auth = new UsernamePasswordAuthenticationToken(...);
            auth.setAuthenticated(true);

            // set authentication in context
            SecurityContextHolder.getContext().setAuthentication(auth);
    }

In your web.xml:

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

<filter-mapping>
    <filter-name>myAuthenticationFilter</filter-name>
    <url-pattern>/fakelogin*</url-pattern>
</filter-mapping>

In your spring.xml:

<bean id="myAuthenticationFilter" class=... />

Another option would be to allow all users to access fakeLogin

<intercept-url pattern="/fakelogin/**" access="permitAll" />

and put the Authentication into the security context in a Web Flow Action.