PDA

Ver la versión completa : Enviar un HTTP POST a web service desde iOS



faraday
11/10/2012, 12:23
Hola,

Aun me cuesta un poco esto del iOS.
Me estoy peleando para enviar un usuario y password a una web que acepta peticiones HTTP.

El problema que tengo es a la hora de enviar el password.
Cómo envío el password hash SHA1 de la contraseña de usuario?

el código que estoy utilizando es este es este:

// Enviamos usuario y password y leemos datos de la web
NSString *postString;
postString = [NSString stringWithFormat:@"user=%@&pass=%@", datoEmail.text, datoPassword.text];

// NSLog(postString);

NSURL *url;
url = [NSURL URLWithString:@"http://www.appadia.com/prueba/login"];
NSMutableURLRequest *req;
req = [NSMutableURLRequest requestWithURL:url];
NSString *msgLength;
msgLength = [NSString stringWithFormat:@"%d", [postString length]];

[req addValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
[req addValue:msgLength forHTTPHeaderField:@"Content-Type"];

[req setHTTPMethod:@"POST"];
[req setHTTPBody:[postString dataUsingEncoding:NSUTF8StringEncoding]];


conn = [[NSURLConnection alloc] initWithRequest:req delegate:self];
if (conn) {
webData = [NSMutableData data];
}

Saludos!

^MiSaTo^
11/10/2012, 12:27
El problema que tienes es que no sabes calcular el sha1?
Si es así: http://www.makebetterthings.com/iphone/how-to-get-md5-and-sha1-in-objective-c-ios-sdk/

Primer resultado de google al poner "sha1 ios".

Google y StackOverflow son tus amigos ;)

faraday
11/10/2012, 12:31
GRACIAS!!

Siempre miro por StackOverflow, pero no tenia ni idea de que era sha1....

Voy a ver si funciona ;)

^MiSaTo^
11/10/2012, 13:23
GRACIAS!!

Siempre miro por StackOverflow, pero no tenia ni idea de que era sha1....

Voy a ver si funciona ;)

No quería sonar borde ni mucho menos ;) (que releyendome quizá sí pudiera parecerlo)

SHA1 es un tipo de encriptación ;)

Eskema
11/10/2012, 14:53
Si usas algo como el mknetwork o el afnetworking, juraria que ya traen metodos para enviar las cosas encriptadas y asi no te calientas la cabeza, el viejo asihttprequest framework (http://allseeing-i.com/ASIHTTPRequest/) (todos ellos disponibles en github) si que traia la encriptacion.
Aunque si eres de los juan palomo yo me lo guiso yo me lo como, entonces no te interesa mirar estos frameworks :)

^MiSaTo^
11/10/2012, 15:07
Hombre para algo tan simple no hace falta tampoco usar un framework ;) Es matar moscas a cañonazos más bien. Aparte NSURLRequest y demás ya han mejorado bastante, yo antes si usaba ASIHTTPRequest pero la verdad que desde iOS 4 (creo) no he vuelto a necesitar usar nada así para request simples.

Eskema
11/10/2012, 15:57
el asihttp es mas para hacer una app completa por lo "gordo" que es, esta claro que para 2 consultas usas el urlrequest y te sobra y te basta. De hecho hice una app de cocina hace un par de meses y toda ella iba con el nsurlrequest sin mas historias, ni frameworks ni leches :)

Dado que aqui el interesado esta aprendiendo le puede venir muy bien examinar esos frameworks y estudiar su codigo, se puede aprender mucho, sobre todo porque hay partes que puedes usar en tu app y crearte unas funciones reusables, ahora mismo yo uso el mknetwork incluso para mierdas como estas tan sencillas porque es muy ligero, soy un p00to vago y tiene cosas muy majas que no tengo tiempo de hacer por mi mismo :)

^MiSaTo^
11/10/2012, 16:05
Yo considero que si está aprendiendo, mejor que aprenda primero cómo se hace a "mano" (que no es dificil) y entienda cómo funciona a que ande enredando con frameworks más "complejos". Básicamente porque esos frameworks internamente usan NSURLRequest y demás, y si no sabemos como funciona lo básico... no empecemos la casa por el tejado no?

Eskema
11/10/2012, 16:08
Tal vez tengas razón y yo es que soy muy bruto, pero creo que le sirven igualmente para aprender, viendo el codigo ves que usan el nsurlrequest, por lo tanto mirando el codigo y complementandolo con la documentacion (de apple sobre nsurlrequest) para entender lo que hace le vale para aprender.
Esto es como todo, cada maestrillo tiene su librillo, a mi me gusta aprender viendo como lo hacen los demás y no pelando la pava con "lo basico" XDD

^MiSaTo^
11/10/2012, 16:16
A programar se aprende programando y pegándote tú con ello, con copiar pegar creo que no aprendes tanto o con sólo leer el código tampoco.
Pero vamos, es mi opinión, que por supuesto no tiene por qué ser ni la única ni la correcta :D
El tema es que luego en las entrevistas de trabajo preguntan como hacer X básico y oh sorpresa como yo siempre uso el framework X... Y yo cuando buscaba gente para contratarla para mi empresa, SIEMPRE miraba que supieran hacer al menos lo básico ;) Porque aprender un framework te lleva minutos si ya sabes lo básico. Pero we, ya digo yo tampoco soy aqui la master del universo y puedo estar equivocada.

Eskema
11/10/2012, 17:14
Yo es que voy al reves del mundo xDD, empiezo por el framework para ver como va y luego bajar "al sotano" para ver como se hace a pelo, en ese punto yo obtengo una idea clara de como va la funcion a pelo y porque fulanito ha creado ese framework y que pretendia con ello, porque si el framework solo me encapsula el urlrequest en una funcion tipo
-(void)HazUnRequestChulo:(nsstring *)paginaWeb; entonces le pueden dar mucho x saco a ese framework, para eso me lo hago yo a pelo :)

^MiSaTo^
11/10/2012, 17:21
Yo suelo seguir la regla de no empezar la casa por el tejado ;)

Eskema
11/10/2012, 17:25
Y por ello tienes todo mi respeto :)

Es la magia de aprender por tu cuenta sin que nadie te guie, lo haces todo al reves y encima mal, pero oye, ¿y lo que se disfruta en el proceso de "haciendo y deshaciendo"? XDDDD

^MiSaTo^
11/10/2012, 17:27
Te recuerdo que ni tengo carrera, ni módulo ni nada de nada XD Yo tb he aprendido por mi cuenta ;)

Eskema
11/10/2012, 19:15
calla perroflauta, que tu ahora eres giri y usas metodos "profesionales" de trabajo, yo estoy en ejpaña usando el metodo ejpañol xDDDD

pakoito
11/10/2012, 20:29
Pues mirad qué casualidad, me hace falta aprender a hacer lo mismo pero desde Android. Una hayudita, compadres? Ya estoy con el goorgle de todas formas.

faraday
11/10/2012, 21:42
Ya consigo encriptar en SHA1 (que no tenia ni idea de lo que era)

El caso es que tengo que hacer una app de prueba para iOS, que entra a una web para leer datos.
Primero hace login con user y pass. Si se loggea correctamente recibe {"success":true,"error":false,"msg":"OK","token":"xxxxxxxxxxxxxxxxxx"}

luego con el token xxxxxxxxxxxxxxxxxx me devuelve una base de datos que tengo que mostrar en el iPhone.
Esto:
Array
(
[success] => 1 [error] => [results] => Array
(
[0] => Array
(
[number] => 31
[sexo] => female
[nombre] => Fidela
[apellido] => Prieto
[calle] => El Roqueo 74
[ciudad] => Las Pedroñeras
[provincia] => Cuenca
[postal] => 16660
[pais] => ES
[email] => FidelaPrietoCasillas@spambob.com [telephonenumber] => 969 010 583
[birthday] => 7/7/1979
[kilograms] => 81.7
[centimeters] => 174
[guid] => d3eeedfb-bdd1-4088-b016-22d5b5d097eb [latitude] => 39.369350
[longitude] => -2.765394
[image] => http://www.appadia.com/prueba/nophoto0.png
)

el caso es que solo consigo hacer login, pero luego ya no se como pasarle el token por [POST] y el limit por [GET]
y menos cuando reciba los datos, convertirlos para mostrar en tabla.

Todo esto para hacer una prueba, a ver si me cogen :S
va a estar muy complicado.....

^MiSaTo^
11/10/2012, 22:24
Cuando pasas algo por get, esto va en la URL directamente, sin embargo por post va en el body de la petición.

Por tanto imaginate que la url es: http://www.laurl.com Usando tu propio código imaginemos que queremos pasar el parámetro limit=10 por get y el token que has dicho por post. Pues sería (te marco en negrita lo que añado a tu código):




// Enviamos usuario y password y leemos datos de la web
NSString *postString;
postString = [NSString stringWithFormat:@"token=%@", variableQueTieneElToken];

// NSLog(postString);

NSURL *url;
url = [NSURL URLWithString:@"http://www.laurl.com?limit=10"]; <- Fíjate que en la URL va el parámetro directamente
NSMutableURLRequest *req;
req = [NSMutableURLRequest requestWithURL:url];
NSString *msgLength;
msgLength = [NSString stringWithFormat:@"%d", [postString length]];

[req addValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
[req addValue:msgLength forHTTPHeaderField:@"Content-Type"];

[req setHTTPMethod:@"POST"];
[req setHTTPBody:[postString dataUsingEncoding:NSUTF8StringEncoding]];


conn = [[NSURLConnection alloc] initWithRequest:req delegate:self];
if (conn) {
webData = [NSMutableData data];
}


Los datos que has pegado... eso es un var_dump de un array de PHP o ese es el formato exacto con el que te viene?

EDIT: No estoy segura al 100% de que tu código no tenga memory leaks o problemas de punteros... Así que yo de ti lo revisaría bien por si acaso que no se ahora mismo de memoria qué metodos te hacen retain automático y cuales no.

-----Actualizado-----


Pues mirad qué casualidad, me hace falta aprender a hacer lo mismo pero desde Android. Una hayudita, compadres? Ya estoy con el goorgle de todas formas.

Qué necesitas hacer exactamente? Un request por post pasando el SHA1 de un password? Porque en Android con el HttpRequest no es nada difícil y supongo que si ya estás mirando en google habrás dado con ello ;)

pakoito
11/10/2012, 22:35
Pues sip, sólo el detallito de hacerlo mejor en un AsyncTask y supongo que un Listener para la respuesta y como dios :P Preocupación--

-----Actualizado-----

Pues sip, sólo el detallito de hacerlo mejor en un AsyncTask y supongo que un Listener para la respuesta y como dios :P Preocupación--

^MiSaTo^
11/10/2012, 23:16
Pues sip, sólo el detallito de hacerlo mejor en un AsyncTask y supongo que un Listener para la respuesta y como dios :P Preocupación--

-----Actualizado-----

Pues sip, sólo el detallito de hacerlo mejor en un AsyncTask y supongo que un Listener para la respuesta y como dios :P Preocupación--

No necesitas ningún listener. Si usas un AsyncTask lo que te devuelva el doInBackground se le pasa al onPostExecute. Ahí es donde puedes parsear la respuesta ;)

Un ejemplo de una request con un AsyncTask podría ser:




class RequestTask extends AsyncTask<String, String, String>{

@Override
protected String doInBackground(String... uri) {
HttpClient httpclient = new DefaultHttpClient();
HttpResponse response;
String responseString = null;
try {
response = httpclient.execute(new HttpGet(uri[0]));
StatusLine statusLine = response.getStatusLine();
if(statusLine.getStatusCode() == HttpStatus.SC_OK){
ByteArrayOutputStream out = new ByteArrayOutputStream();
response.getEntity().writeTo(out);
out.close();
responseString = out.toString();
} else{
response.getEntity().getContent().close();
throw new IOException(statusLine.getReasonPhrase());
}
} catch (ClientProtocolException e) {
//TODO Handle exception
} catch (IOException e) {
//TODO Handle exception
}
return responseString;
}

@Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
//Aquí el código para parsear la respuesta, por ejemplo:
parseResponseString(result);
}
}



Yo las AsyncTask a no ser que sea algo que se va a usar desde muchos sitios, las suelo poner como inner class del activity/clase que sea.

faraday
12/10/2012, 11:23
Ya se que soy un pesado, pero ayudarme con esto porfavor....
Tengo la función que convierte el password en formato sha1:

-(NSString *) sha1:(NSString *)input
{
const char *cstr = [input cStringUsingEncoding:NSUTF8StringEncoding];
NSData *data = [NSData dataWithBytes:cstr length:input.length];

uint8_t digest[CC_SHA1_DIGEST_LENGTH];

CC_SHA1(data.bytes, data.length, digest);

NSMutableString* output = [NSMutableString stringWithCapacity:CC_SHA1_DIGEST_LENGTH * 2];

for(int i = 0; i < CC_SHA1_DIGEST_LENGTH; i++)
[output appendFormat:@"%02x", digest[i]];

return output;
}

Luego la hago funcionar como he visto, pero no me funciona, me da un error:

NSString *inputStr =@"the string to crypt";
NSString *outputSHA1Str;
outputSHA1Str= sha1(inputStr);

Alguna idea?
Gracias !! ;)

^MiSaTo^
12/10/2012, 11:32
Si no pegas el error que te da... tampoco puedo decirte mucho xD

faraday
12/10/2012, 11:56
28914
este es el error....

--------------------------------------------------------

Bueno, por fin lo he arreglado.

He cambiado de método para codificar el password

NSString *hashkey = <dato a codificar>;
// PHP uses ASCII encoding, not UTF
const char *s = [hashkey cStringUsingEncoding:NSASCIIStringEncoding];
NSData *keyData = [NSData dataWithBytes:s length:strlen(s)];

// This is the destination
uint8_t digest[CC_SHA1_DIGEST_LENGTH] = {0};
// This one function does an unkeyed SHA1 hash of your hash data
CC_SHA1(keyData.bytes, keyData.length, digest);

// Now convert to NSData structure to make it usable again
NSData *out = [NSData dataWithBytes:digest length:CC_SHA1_DIGEST_LENGTH];
// description converts to hex but puts <> around it and spaces every 4 bytes
NSString *hash = [out description];
hash = [hash stringByReplacingOccurrencesOfString:@" " withString:@""];
hash = [hash stringByReplacingOccurrencesOfString:@"<" withString:@""];
hash = [hash stringByReplacingOccurrencesOfString:@">" withString:@""];
// hash is now a string with just the 40char hash value in it

el hash es el código en sha1 ;)

^MiSaTo^
12/10/2012, 13:38
Es un poco "sucio" convertir a hexadecimal de esa manera la verdad porque el método description te da la "descripción" del objeto en hexadecimal. Esto suele ser normalmente la posición de memoria en la que se encuentra el objeto, y aunque en este caso concreto si te devuelve el contenido de NSData en hexadecimal, no aconsejan usarlo desde Apple dado que el método description podría cambiar también en un futuro ya que no está pensado para eso (sino para la descripción del objeto en sí).
Por otro lado, si el NSData ya contiene una cadena en hexadecimal, el pasarlo a un string es trivial usando el método initWithData de NSString.
Por último la mejor manera de hacerlo sería crearte una categoría de NSData como sugieren aquí (http://stackoverflow.com/questions/1305225/best-way-to-serialize-a-nsdata-into-an-hexadeximal-string).