Question

I am trying to use the Wink RestClient to do functional testing on a Rest service endpoint. I use mocks for unit testing but I'd like to functionally test it as an endpoint consumer.

I understand some will object to me calling it a REST endpoint while using form-based auth but that is the current architecture I have.

The majority of the resources I want to test are protected resources and the application (running on Tomcat6) is protected by form authentication. (as in the below web.xml snippet).

What I've tried so far is to make an initial call to an unprotected resource, to obtain the set-cookie header, that contains JSESSIONID, and use that JSESSIONID in the header ( via Resource.cookie() ) in subsequent requests but that does not yield fruit.

web.xml

<login-config>
    <auth-method>FORM</auth-method>
    <form-login-config>
        <form-login-page>/login.html</form-login-page>
        <form-error-page>/login.html?failure=true</form-error-page>
    </form-login-config>
</login-config>

My Wink RestClient code looks like below. All responses are 200, but two things I notice are that the response from the call to /j_security_check/ does not include the jsessionid cookie, and the call to the protected resource said I had a signin failure. The payload for the call to j_security_check was captured directly from a previous successful browser request intercepted.

ClientConfig config = new ClientConfig();
config.setBypassHostnameVerification(true);
RestClient restClient = new RestClient(config);

Resource unprotectedResource = restClient.resource( BASE_URL + "/");
unprotectedResource.header( "Accept", "*/*" );
ClientResponse clientResponse = unprotectedResource.get();
String response = clientResponse.getEntity(String.class);

// get jSession ID
String jSessionId = clientResponse.getHeaders().get("set-cookie").get(0);
jSessionId = jSessionId.split(";")[0];
System.out.println(jSessionId);

// create a request to login via j_security_check
Resource loginResource = restClient.resource(BASE_URL + "/j_security_check/");
loginResource.accept("text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
loginResource.header("referer", "http://localhost:8080/contextroot/");
loginResource.cookie( jSessionId );
loginResource.header("Connection", "keep-alive");
loginResource.header("Content-Type", "application/x-www-form-urlencoded");
loginResource.header("Content-Length", "41");

ClientResponse loginResponse = loginResource.post("j_username=*****&j_password=*************");

/*  the loginResponse, as this point, does not have the jsessionid cookie, my browser client does */

Resource protectedResource = restClient.resource(BASE_URL + "/protected/test/");
systemResource.accept("application/json");
systemResource.cookie( jSessionId );

ClientResponse systemResponse = systemResource.get();
response = clientResponse.getEntity(String.class);
System.out.println(response);

Any thoughts or experience with using the Wink RestClient to exercise form-auth-protected resources would be greatly appreciated. I suppose I'd entertain other frameworks, I have heard of REST-Assured and others, but since the application uses Wink and the RestClient seems to provide me with what I need, I figured I'd stick with it.

Was it helpful?

Solution

Found the problem, and the solution

j_security_check was responding to my POST request (to authenticate), with a #302/redirect. That was being followed by the wink RestClient, but my JSESSIONID cookie was not being appended to it. That was causing the response (from the redirected URL) to contain a set-cookie header, with a new header. My subsequent calls, into which I inserted the JSESSIONID from the first call, failed, because that cookie was expired. All I needed to do was instruct the RestClient to NOT follow redirects. If the redirect were necessary, I would construct it on my own, containing the appropriate cookie.

Chromium and Firefox carry the cookie from the original request to the redirected request so it's all good.

Here is some code that worked for me, using JUnit4, RestClient from the Apache Wink project (and a Jackson ObjectMapper)

@Test
public void testGenerateZipEntryName() throws JsonGenerationException, JsonMappingException, IOException
{
    final ObjectMapper mapper = new ObjectMapper();

    final String BASE_URL = "http://localhost:8080/rest";

    // Configure the Rest client
    ClientConfig config = new ClientConfig();
    config.proxyHost("localhost");    // helpful when sniffing traffic
    config.proxyPort(50080);          // helpful when sniffing traffic
    config.followRedirects(false);    // This is KEY for form auth
    RestClient restClient = new RestClient(config);


    // Get an unprotected resource -- to get a JSESSIONID
    Resource resource = restClient.resource( BASE_URL + "/");
    resource.header( "Accept", "*/*" );
    ClientResponse response = resource.get();
    // extract the jSession ID, in a brittle and ugly way
    String jSessId = response.getHeaders().get("set-cookie").get(0).split(";")[0].split("=")[1];


    // Get the login resource *j_security_check*
    resource = restClient.resource(BASE_URL + "/j_security_check");
    resource.cookie("j_username_tmp=admin; j_password_tmp=; JSESSIONID=" + jSessId);
    resource.header("Content-Type", "application/x-www-form-urlencoded");
    resource.header("Content-Length", "41");

    // Verify that login resource redirects us
    response = resource.post("j_username=admin&j_password=***********");
    assertTrue( response.getStatusCode() == 302 );


    // Grab a public resource
    resource = restClient.resource(BASE_URL + "/");
    resource.cookie("j_username_tmp=admin; j_password_tmp=; JSESSIONID=" + jSessId);
    response = resource.get();
    // verify status of response
    assertTrue( response.getStatusCode() == 200 );


    // Grab a protected resource
    resource = restClient.resource(BASE_URL + "/rest/system");
    resource.cookie("j_username_tmp=admin; j_password_tmp=; JSESSIONID=" + jSessId);

    // Verify resource returned OK
    response = resource.contentType("application/json").accept("*/*").get();
    assertTrue( response.getStatusCode() == 200 );

    // Deserialize body of protected response into domain object for further testing 
    MyObj myObj = mapper.readValue(response.getEntity(String.class), MyObj.class );
    assertTrue( myObj.customerArchived() == false );
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top