user1391281
user1391281

Reputation: 215

HTTP Request swift providing parameters

For a simple iOS (swift) application for my university I try to login to one of their pages to retrieve the amount of money currently on my card. However when doing my http request I can't get the data I need.

This is my code:

let url = NSURL(string: "https://campuscard.hhs.nl/portal/j_spring_security_check")
let request = NSMutableURLRequest(URL: url)

request.HTTPMethod = "POST"
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")

let data : NSData = ("?j_username=USERNAME&j_password=PASSWORD").dataUsingEncoding(NSUTF8StringEncoding)!;
request.HTTPBody = data;

NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.mainQueue()) {(response, data, error) in
    println(NSString(data: data, encoding: NSUTF8StringEncoding))
}

It gives me the error that I have enterred the wrong credentials and when I print my request it says:

<NSMutableURLRequest: 0x7f8d7b53bd30> { URL: https://campuscard.hhs.nl/portal/j_spring_security_check, headers: {
"Content-Type" = "application/x-www-form-urlencoded";
} }

So I think it doesn't include the username and password.

Does anyone have an idea?

It would be much appreciated by me and other students on my university!

added

Me and a friend of my class we see the attributes in the request through Charles thanks to you, however since we both never tried working with this we don't know how to handle those attributes. We simply added all we can find to the request and tried it but we still get the ArrayOutOfBoundsException on the server.

var dataString = "j_username=USERNAME&j_password=PASSWORD"
    var request : NSMutableURLRequest = NSMutableURLRequest()
    request.URL = NSURL(string: "https://campuscard.hhs.nl/portal/j_spring_security_check")
    var postString = (dataString as NSString).dataUsingEncoding(NSUTF8StringEncoding)

    request.HTTPMethod = "POST"

    request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
    //request.setValue("JSESSIONID=C78C688403A836968EC1FEAED9AE9126", forHTTPHeaderField: "Cookie")
    request.setValue("campuscard.hhs.nl", forHTTPHeaderField: "Host");
    request.setValue("keep-alive", forHTTPHeaderField: "Connection");
    request.setValue("41", forHTTPHeaderField: "Content-Length");
    request.setValue("max-age=0", forHTTPHeaderField: "Cache-Controle");
    request.setValue("text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", forHTTPHeaderField: "Accept");
    request.setValue("https://campuscard.hhs.nl", forHTTPHeaderField: "Origin");
    request.setValue("https://campuscard.hhs.nl/portal/login", forHTTPHeaderField: "Referer");
    request.setValue("gzip,deflate", forHTTPHeaderField: "Accept-Encoding");
    request.setValue("nl-NL,nl;q=0.8,en-US;q=0.6,en;q=0.4", forHTTPHeaderField: "Accept-Language");
    request.HTTPBody = postString

    NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.mainQueue()) {(response, data, error) in
        println(NSString(data: data, encoding: NSUTF8StringEncoding))
    }

I'm sorry to place such a big piece of code on you, but maybe there is something you can see is wrong. Thank you for your time

Upvotes: 3

Views: 4826

Answers (1)

Rob
Rob

Reputation: 437412

The body of the x-www-form-urlencoded request should not contain the ?.


As an aside, you should be percent encoding USERNAME and PASSWORD. Right now, if either (more likely, the password) contained certain reserved characters, your request would fail. I use a extension like this in Swift 2:

extension String {

    /// Percent escape value to be added to a HTTP request
    ///
    /// This percent-escapes all characters besize the alphanumeric character set and "-", ".", "_", and "*".
    /// This will also replace spaces with the "+" character as outlined in the application/x-www-form-urlencoded spec:
    ///
    /// http://www.w3.org/TR/html5/forms.html#application/x-www-form-urlencoded-encoding-algorithm
    ///
    /// - returns: Return percent escaped string.

    func stringByAddingPercentEncodingForFormUrlencoded() -> String? {
        let allowedCharacters = NSCharacterSet(charactersInString: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._* ")

        return stringByAddingPercentEncodingWithAllowedCharacters(allowedCharacters)?.stringByReplacingOccurrencesOfString(" ", withString: "+")
    }
}

I use this stringByAddingPercentEncodingForFormUrlencoded function on the USERNAME and PASSWORD values (but not the whole string).

Or, in Swift 3:

extension String {

    /// Percent escape value to be added to a HTTP request
    ///
    /// This percent-escapes all characters besize the alphanumeric character set and "-", ".", "_", and "*".
    /// This will also replace spaces with the "+" character as outlined in the application/x-www-form-urlencoded spec:
    ///
    /// http://www.w3.org/TR/html5/forms.html#application/x-www-form-urlencoded-encoding-algorithm
    ///
    /// - returns: Return percent escaped string.

    func addingPercentEncodingForFormUrlencoded() -> String? {
        let allowedCharacters = CharacterSet(charactersIn: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._* ")

        return addingPercentEncoding(withAllowedCharacters: allowedCharacters)?.replacingOccurrences(of: " ", with: "+")
    }
}

The absence of the username and password when examining NSURLRequest is not at all worrying (I wouldn't have expected it to include the body of the request when you log it like that). If you want to check, run this through Charles or something like that.

If you're using Charles, if you want to inspect HTTPS interaction, you have to enable SSL proxying, and add your domain to the list of location. See "Proxy settings..." on "Proxy" menu, and click on the "SSL" tab. See Charles Web Debugging Proxy.

This will show you the full request in all of its glory. If you're trying to have your app log on like you would from a web browser, you can use Charles to watch the web browser exchange and compare and contrast that to your app.


In your revised question, you are now showing all the various headers that you're trying to set. (You don't have to set some of these: Watch existing app request in Charles and you'll see some of these are already set.) I'd be surprised if any of these are needed.

Ironically, the only one that's probably critical is the one you've commented out, JSESSIONID. lol. Many of these web sites will provide some session ID in the login HTML. Then when you then try to submit the login request, you have to pass the same JSESSIONID that was provided to you by the login HTML page.

So the model is usually (a) get the login page; (b) parse it for whatever header fields that need to be set in subsequent requests (e.g. looks like it might be JSESSIONID, on the basis of your example); and (c) supply that session id for all subsequent requests.

This is supposition, as I haven't been able to actually see the full conversation b/w the web browser and your particular web server, but this is the sort of pattern I've seen before. Just watch web browser requests/responses, paying special attention to cryptic id numbers buried in the HTML that might be provided in subsequent requests (either in the body or the headers).

Upvotes: 6

Related Questions