http post data to servlet with form-based authentication

Accessing web pages or servlet which are not authenticated or which use basic authentication is relatively easy, especially when using the commons-httpclient library.

However, things get more tricky once the page you try to access is using form based authentication. Unfortunately, httpclient does not handle that automatically, so you have to code this yourself.

This can be done with the following code which handles both the cases of BASIC and FORM based authentication.


    public String getPage( String urlString, ImportCredentials credentials )
        throws Exception
    {
        HttpClient httpConn = new HttpClient();
        httpConn.getParams().setAuthenticationPreemptive( true );
        httpConn.getState().setCredentials( AuthScope.ANY, new Credentials( credentials ) );
        HttpConnectionManagerParams conpar = new HttpConnectionManagerParams();
        conpar.setConnectionTimeout( 5 * 60000 ); // 5 minutes
        conpar.setSoTimeout( 5 * 60000 ); // 5 minutes
        httpConn.getHttpConnectionManager().setParams( conpar );
        GetMethod get = new GetMethod( urlString );
        get.getParams().setCookiePolicy( CookiePolicy.BROWSER_COMPATIBILITY );
        get.setDoAuthentication( true );
        httpConn.executeMethod( get );
        String responseBody = get.getResponseBodyAsString();
        if ( responseBody.contains( "j_security_check" ) )
        {
            NameValuePair[] data = new NameValuePair[2];
            data[ 0 ] = new NameValuePair( "j_username", credentials.getUsername() );
            data[ 1 ] = new NameValuePair( "j_password", credentials.getPassword() );
            String loginUrl = urlString;
            while ( loginUrl.length() > 0 && loginUrl.charAt( loginUrl.length() - 1 ) == '/' )
            {
                loginUrl = loginUrl.substring( 0, loginUrl.length() - 1 );
            }
            PostMethod authpost = new PostMethod( loginUrl + "/j_security_check" );
            authpost.setRequestBody( data );

            //Release Get Connection
            get.releaseConnection();

            int httpRes = httpConn.executeMethod( authpost );
            responseBody = authpost.getResponseBodyAsString();
            if ( httpRes == 301 || httpRes == 302 || httpRes == 307 )
            {
                // redirected, get content page
                get = new GetMethod( authpost.getResponseHeader( "Location" ).getValue() );
                get.setRequestHeader( "Content-Type", "text/plain; charset=UTF-8" );
                authpost.releaseConnection();
                httpConn.executeMethod( get );
                responseBody = get.getResponseBodyAsString();
                get.releaseConnection();
            }
        }
        else
        {
            get.releaseConnection();
        }
        return responseBody;
    }

Unfortunately I fear this is application server specific. It may be that some application servers do not have a page called “j_security_check”. In that case the test and authentication URL will need to be modified.

The example above is is the simple case where you are just getting a page. It gets more tricky if the original request is a http POST. Basically, you could just replace the original get by a post, but this can give character set problems. The servlet engine does not seem to remember the character encoding across the different requests (and explicitly setting them does not work either), so the only solution is to do a dummy request first, just to assure the communication is authenticated, and then do the “real” request with the correct character encoding.

The end result (from the equanda ImportUtil class) looks like this:


   public static String importData( String importData, String urlString, ImportCredentials credentials )
        throws Exception
    {
        HttpClient httpConn = new HttpClient();
        httpConn.getParams().setAuthenticationPreemptive( true );
        httpConn.getState().setCredentials( AuthScope.ANY, new Credentials( credentials ) );
        HttpConnectionManagerParams conpar = new HttpConnectionManagerParams();
        conpar.setConnectionTimeout( 5 * 60000 ); // 5 minutes
        conpar.setSoTimeout( 5 * 60000 ); // 5 minutes
        httpConn.getHttpConnectionManager().setParams( conpar );
        PostMethod post = new PostMethod( urlString );
        post.setRequestHeader( "Content-Type", "text/plain; charset=UTF-8" );
        post.getParams().setCookiePolicy( CookiePolicy.BROWSER_COMPATIBILITY );
        post.setDoAuthentication( true );

        // no data yet, need to assure the authentication is done first
        httpConn.executeMethod( post );
        //get response
        String responseBody = post.getResponseBodyAsString();
        if ( responseBody.contains( "j_security_check" ) )
        {
            NameValuePair[] data = new NameValuePair[2];
            data[ 0 ] = new NameValuePair( "j_username", credentials.getUsername() );
            data[ 1 ] = new NameValuePair( "j_password", credentials.getPassword() );
            String loginUrl = urlString;
            while ( loginUrl.length() > 0 && loginUrl.charAt( loginUrl.length() - 1 ) == '/' )
            {
                loginUrl = loginUrl.substring( 0, loginUrl.length() - 1 );
            }
            PostMethod authpost = new PostMethod( loginUrl + "/j_security_check" );
            authpost.setRequestBody( data );

            //Release Get Connection
            post.releaseConnection();

            int httpRes = httpConn.executeMethod( authpost );
            if ( httpRes == 301 || httpRes == 302 || httpRes == 307 )
            {
                // redirected, get content page
                GetMethod get = new GetMethod( authpost.getResponseHeader( "Location" ).getValue() );
                authpost.releaseConnection();
                httpConn.executeMethod( get );
                get.releaseConnection();
            }
        }
        else
        {
            post.releaseConnection();
        }

        // now do the real post, as otherwise the character set is not remembered
        post = new PostMethod( urlString );
        post.setRequestHeader( "Content-Type", "text/plain; charset=UTF-8" );
        post.setRequestBody( importData );
        httpConn.executeMethod( post );
        responseBody = post.getResponseBodyAsString();
        post.releaseConnection();
        
        return responseBody;
    }

Leave a Reply

Your email address will not be published. Required fields are marked *

question razz sad evil exclaim smile redface biggrin surprised eek confused cool lol mad twisted rolleyes wink idea arrow neutral cry mrgreen

*