jueves, 28 de febrero de 2013

Restful spring roo - Spring Security - Cliente Jersey -

En esta oportunidad voy a explicar cómo crear servicios restful en una aplicación generada mediante spring roo y desarrollaré una aplicación cliente jersey que lea la data expuesta en los servicios restful, no solamente leeré la data sino que demostraré como podemos editar un atributo y enviarlo de nuevo mediante un servicio rest con método PUT para su actualización en la base de datos. También explicaré como configurar spring security de tal manera que para las urls que expongan los servicios rest queden aseguradas pero no redirijan a la página de login, dejando de esta manera la página de login para los otros módulos que estén asegurados. Voy a omitir los pasos de configuración de la base de datos y de ingeniería reversa de mis tablas. Solamente trabajaremos con una tabla llamada ejemplo la cual creó una entidad Ejemplo.java.

A continuación los pasos a seguir y cumplir con los requerimientos expuestos:

1. Ejecutamos el comando json add:
 json add --class ~.model.Ejemplo
Este comando introducirá la anotación @RooJson en el tipo de destino especificado:

@RooJson
public class Ejemplo {

}

Y creara el siguiente archivo .aj donde están declarados todos los métodos que convertirán la entidad a json y viceversa:
package org.springroo.model;

import flexjson.JSONDeserializer;
import flexjson.JSONSerializer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.springroo.model.Ejemplo;


privileged aspect Ejemplo_Roo_Json { 

public String Ejemplo.toJson() {

return new JSONSerializer().exclude("*.class").serialize(this);

}

public static Ejemplo Ejemplo.fromJsonToEjemplo(String json) {

return new JSONDeserializer<Ejemplo>().use(null, Ejemplo.class).deserialize(json);

}

public static String Ejemplo.toJsonArray(Collection<Ejemplo> collection) {

return new JSONSerializer().exclude("*.class").serialize(collection);

}

public static Collection<Ejemplo> Ejemplo.fromJsonArrayToEjemploes(String json) {

return new JSONDeserializer<List<Ejemplo>>().use(null, ArrayList.class).use("values", Ejemplo.class).deserialize(json);

}

}


2. Crear un controllador donde se expondrán los servicios rest mediante el siguiente comando:
controller class --class ~.web.EjemploController --preferredMapping /restAPI/ejemplo


Esto creara la siguiente clase java:

package org.springroo.web;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springroo.model.Ejemplo;

@RequestMapping("/restAPI/ejemplo")
@Controller
public class EjemploController {

}


3. Ejecutamos el comando web mvc json add:
web mvc json add --jsonObject ~.model.Ejemplo --class ~.web.EjemploController

Este comando introduce la anotación @RooWebJson en el tipo de destino especificado.

package org.springroo.web;

import org.springframework.roo.addon.web.mvc.controller.json.RooWebJson;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springroo.model.Ejemplo;


@RequestMapping("/restAPI/ejemplo")
@Controller
@RooWebJson(jsonObject = Ejemplo.class)
public class EjemploController {

}

Creando este archivo .aj con los métodos crud restful:

// WARNING: DO NOT EDIT THIS FILE. THIS FILE IS MANAGED BY SPRING ROO.
// You may push code into the target .java compilation unit if you wish to edit any member(s).

package org.springroo.web;

import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springroo.model.Ejemplo;
import org.springroo.services.EjemploService;
import org.springroo.web.EjemploController;

privileged aspect EjemploController_Roo_Controller_Json {
    
    @Autowired
    EjemploService EjemploController.EjemploService;
    
    @RequestMapping(value = "/{id}", headers = "Accept=application/json")
    @ResponseBody
    public ResponseEntity EjemploController.showJson(@PathVariable("id") Integer id) {
        Ejemplo Ejemplo = EjemploService.findEjemplo(id);
        HttpHeaders headers = new HttpHeaders();
        headers.add("Content-Type", "application/json; charset=utf-8");
        if (Ejemplo == null) {
            return new ResponseEntity(headers, HttpStatus.NOT_FOUND);
        }
        return new ResponseEntity(Ejemplo.toJson(), headers, HttpStatus.OK);
    }
    
    @RequestMapping(headers = "Accept=application/json")
    @ResponseBody
    public ResponseEntity EjemploController.listJson() {
        HttpHeaders headers = new HttpHeaders();
        headers.add("Content-Type", "application/json; charset=utf-8");
        List result = EjemploService.findAllEjemploes();
        return new ResponseEntity(Ejemplo.toJsonArray(result), headers, HttpStatus.OK);
    }
    
    @RequestMapping(method = RequestMethod.POST, headers = "Accept=application/json")
    public ResponseEntity EjemploController.createFromJson(@RequestBody String json) {
        Ejemplo Ejemplo = Ejemplo.fromJsonToEjemplo(json);
        EjemploService.saveEjemplo(Ejemplo);
        HttpHeaders headers = new HttpHeaders();
        headers.add("Content-Type", "application/json");
        return new ResponseEntity(headers, HttpStatus.CREATED);
    }
    
    @RequestMapping(value = "/jsonArray", method = RequestMethod.POST, headers = "Accept=application/json")
    public ResponseEntity EjemploController.createFromJsonArray(@RequestBody String json) {
        for (Ejemplo Ejemplo: Ejemplo.fromJsonArrayToEjemploes(json)) {
            EjemploService.saveEjemplo(Ejemplo);
        }
        HttpHeaders headers = new HttpHeaders();
        headers.add("Content-Type", "application/json");
        return new ResponseEntity(headers, HttpStatus.CREATED);
    }
    
    @RequestMapping(method = RequestMethod.PUT, headers = "Accept=application/json")
    public ResponseEntity EjemploController.updateFromJson(@RequestBody String json) {
        HttpHeaders headers = new HttpHeaders();
        headers.add("Content-Type", "application/json");
        Ejemplo Ejemplo = Ejemplo.fromJsonToEjemplo(json);
        if (EjemploService.updateEjemplo(Ejemplo) == null) {
            return new ResponseEntity(headers, HttpStatus.NOT_FOUND);
        }
        return new ResponseEntity(headers, HttpStatus.OK);
    }
    
    @RequestMapping(value = "/jsonArray", method = RequestMethod.PUT, headers = "Accept=application/json")
    public ResponseEntity EjemploController.updateFromJsonArray(@RequestBody String json) {
        HttpHeaders headers = new HttpHeaders();
        headers.add("Content-Type", "application/json");
        for (Ejemplo Ejemplo: Ejemplo.fromJsonArrayToEjemploes(json)) {
            if (EjemploService.updateEjemplo(Ejemplo) == null) {
                return new ResponseEntity(headers, HttpStatus.NOT_FOUND);
            }
        }
        return new ResponseEntity(headers, HttpStatus.OK);
    }
    
    @RequestMapping(value = "/{id}", method = RequestMethod.DELETE, headers = "Accept=application/json")
    public ResponseEntity EjemploController.deleteFromJson(@PathVariable("id") Integer id) {
        Ejemplo Ejemplo = EjemploService.findEjemplo(id);
        HttpHeaders headers = new HttpHeaders();
        headers.add("Content-Type", "application/json");
        if (Ejemplo == null) {
            return new ResponseEntity(headers, HttpStatus.NOT_FOUND);
        }
        EjemploService.deleteEjemplo(Ejemplo);
        return new ResponseEntity(headers, HttpStatus.OK);
    }
    
}


4. Asegurando nuestra aplicación mediante el siguiente comando:
spring security setup

Este comando creara el siguiente archivo de configuración: applicationContext-security.xml

Configuración que asegura nuestras urls de servicios rest pero sin redirigir a nuestro modulo de login:
<!-- HTTP security configurations -->
<http pattern="/restAPI/**" create-session="stateless" authentication-manager-ref="authenticationManager">
<intercept-url pattern="/restAPI/**" access="ROLE_ADMIN" />
<http-basic />
</http>

Y dejamos la configuración por defecto que nos genero spring roo:

<http auto-config="true" use-expressions="true" create-session="always">
<form-login login-processing-url="/resources/j_spring_security_check" login-page="/login" authentication-failure-url="/login?login_error=t" />
<logout logout-url="/resources/j_spring_security_logout" />
<!-- Configure these elements to secure URIs in your application -->
<intercept-url pattern="/choices/**" access="hasRole('ROLE_ADMIN')" />
<intercept-url pattern="/member/**" access="isAuthenticated()" />
<intercept-url pattern="/resources/**" access="permitAll" />
<intercept-url pattern="/**" access="permitAll" />
</http>


5. Probamos nuestras configuraciones en el navegador y la salida de nuestros servicios rest:

Intentando acceder a un modulo seguro nos muestra el login form:




Intentado acceder a una url segura de nuestros servicios rest:





Consultando el registro con id = 1 de nuestra tabla ejemplo veamos la salida en nuestro navegador:
url: http://localhost:8080/appXXXXXXX/restAPI/ejemplo/1 muestra la siguiente salida:

{"id":1,"idejemplo":1,"t1":"","t11":"","t12":"","t13":"","t14":"","t15":"","t16":"","t17":"","t18":"","t2":"","t3":"","t4":"","t5":"","t6":"","t7":"","t8":"","text1":"WILLIAMS","text10":"","text11":"","text12":"","text13":"","text14":"","text15":"","text16":"","text17":"","text18":"","text19":"","text20":"","text21":"","text22":"","text23":"","text24":"","text25":"","text26":"","text27":"","text28":"","text29":"","text3":"","text30":"","text31":"","text32":"","text33":"","text34":"","text35":"","text36":"","text39":"","text4":"","text40":"","text41":"","text42":"","text43":"","text44":"","text45":"","text46":"","text47":"","text48":"","text49":null,"text5":"","text50":"","text52":"","text6":"","text7":"","text8":"","text9":""}

Consultando todos los registros:
url: http://localhost:8080/appNegocioIribarren/restAPI/ejemplo muestra la siguiente salida:

[{"id":2,"idejemplo":2,"t1":null,"t11":null,"t12":null,"t13":null,"t14":null,"t15":null,"t16":null,"t17":null,"t18":null,"t2":null,"t3":null,"t4":null,"t5":null,"t6":null,"t7":null,"t8":null,"text1":null,"text10":null,"text11":null,"text12":null,"text13":null,"text14":null,"text15":null,"text16":null,"text17":null,"text18":null,"text19":null,"text20":null,"text21":null,"text22":null,"text23":null,"text24":null,"text25":null,"text26":null,"text27":null,"text28":null,"text29":null,"text3":null,"text30":null,"text31":null,"text32":null,"text33":null,"text34":null,"text35":null,"text36":null,"text39":null,"text4":null,"text40":null,"text41":null,"text42":null,"text43":null,"text44":null,"text45":null,"text46":null,"text47":null,"text48":null,"text49":null,"text5":null,"text50":null,"text52":null,"text6":null,"text7":null,"text8":null,"text9":null},{"id":3,"idejemplo":3,"t1":"","t11":"","t12":"","t13":"","t14":"","t15":"","t16":"","t17":"","t18":"","t2":"","t3":"","t4":"","t5":"","t6":"","t7":"","t8":"","text1":"","text10":"","text11":"","text12":"","text13":"","text14":"","text15":"","text16":"","text17":"","text18":"","text19":"","text20":"","text21":"","text22":"","text23":"","text24":"","text25":"","text26":"","text27":"","text28":"","text29":"","text3":null,"text30":"","text31":"","text32":"","text33":"","text34":"","text35":"","text36":"","text39":"","text4":"","text40":"","text41":"","text42":"","text43":"","text44":"","text45":"","text46":"","text47":null,"text48":"","text49":null,"text5":"","text50":"","text52":null,"text6":"","text7":"","text8":"","text9":""},{"id":1,"idejemplo":1,"t1":"","t11":"","t12":"","t13":"","t14":"","t15":"","t16":"","t17":"","t18":"","t2":"","t3":"","t4":"","t5":"","t6":"","t7":"","t8":"","text1":"WILLIAMS","text10":"","text11":"","text12":"","text13":"","text14":"","text15":"","text16":"","text17":"","text18":"","text19":"","text20":"","text21":"","text22":"","text23":"","text24":"","text25":"","text26":"","text27":"","text28":"","text29":"","text3":"","text30":"","text31":"","text32":"","text33":"","text34":"","text35":"","text36":"","text39":"","text4":"","text40":"","text41":"","text42":"","text43":"","text44":"","text45":"","text46":"","text47":"","text48":"","text49":null,"text5":"","text50":"","text52":"","text6":"","text7":"","text8":"","text9":""}]

6.Agreamos la siguiente dependencia en nuestro archivo pom.xml la necesitaremos para comenzar a desarrollar nuestra aplicación cliente:

<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-client</artifactId>
<version>1.8</version>
</dependency>


7. Creando nuestra aplicación cliente mediante la api de jersey client, la explicaré mediante el mismo código:

package org.springroo.test;

import java.io.IOException;

import javax.naming.AuthenticationException;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.client.WebResource;
import com.sun.star.lang.IllegalArgumentException;


public class ClientJersey {

 public static void main(String[] args) throws IOException,
   AuthenticationException {
  Client client = Client.create();
  try {
   String auth = new sun.misc.BASE64Encoder().encode("admin:admin"
     .getBytes());
   WebResource webResource = client
     .resource("http://192.168.1.103:8080/appXXXXXX/restAPI/ejemplo");
   ClientResponse response = webResource
     .header("Authorization", "Basic " + auth)
     .type("application/json").accept("application/json")
     .get(ClientResponse.class);

   int statusCode = response.getStatus();
   if (statusCode == 401) {
    throw new AuthenticationException(
      "Invalid Username or Password");
   }
   if (statusCode == 403) {
    throw new AuthenticationException("Usuario no autorizado");
   }
//Obtenemos el arreglo de objetos Ejemplo y lo recorremos para obtener el objeto de id = 1
   JSONArray ja = new JSONArray(response.getEntity(String.class));
   JSONObject joForUpdate = null;
   for (int i = 0; i < ja.length(); i++) {
    if (ja.getJSONObject(i).getInt("id") == 1) {
     joForUpdate = ja.getJSONObject(i);
     break;
    }
   }
   if (joForUpdate == null)
    throw new IllegalArgumentException("json object null");

   System.out.println(joForUpdate.get("id"));
   joForUpdate.put("text1", "RIVAS");

   // Luego de obtener el objeto de id = 1 realizamos la actualizacion de su atributo text1 
   response = webResource.header("Authorization", "Basic " + auth)
     .type("application/json").accept("application/json")
     .put(ClientResponse.class, joForUpdate.toString());

   System.out.println("Estatus: " + response.getStatus());

  } catch (JSONException e) {
   e.printStackTrace();
  } catch (IllegalArgumentException e) {
   e.printStackTrace();
  } finally {
   client.destroy();
  }

 }

}

8. Verificación de si el atributo text1 del registro con id = 1 cambio:
url: http://localhost:8080/appXXXXXX/restAPI/ejemplo

[{"id":2,"idejemplo":2,"t1":null,"t11":null,"t12":null,"t13":null,"t14":null,"t15":null,"t16":null,"t17":null,"t18":null,"t2":null,"t3":null,"t4":null,"t5":null,"t6":null,"t7":null,"t8":null,"text1":null,"text10":null,"text11":null,"text12":null,"text13":null,"text14":null,"text15":null,"text16":null,"text17":null,"text18":null,"text19":null,"text20":null,"text21":null,"text22":null,"text23":null,"text24":null,"text25":null,"text26":null,"text27":null,"text28":null,"text29":null,"text3":null,"text30":null,"text31":null,"text32":null,"text33":null,"text34":null,"text35":null,"text36":null,"text39":null,"text4":null,"text40":null,"text41":null,"text42":null,"text43":null,"text44":null,"text45":null,"text46":null,"text47":null,"text48":null,"text49":null,"text5":null,"text50":null,"text52":null,"text6":null,"text7":null,"text8":null,"text9":null},{"id":3,"idejemplo":3,"t1":"","t11":"","t12":"","t13":"","t14":"","t15":"","t16":"","t17":"","t18":"","t2":"","t3":"","t4":"","t5":"","t6":"","t7":"","t8":"","text1":null,"text10":"","text11":"","text12":"","text13":"","text14":"","text15":"","text16":"","text17":"","text18":"","text19":"","text20":"","text21":"","text22":"","text23":"","text24":"","text25":"","text26":"","text27":"","text28":"","text29":"","text3":null,"text30":"","text31":"","text32":"","text33":"","text34":"","text35":"","text36":"","text39":"","text4":"","text40":"","text41":"","text42":"","text43":"","text44":"","text45":"","text46":"","text47":null,"text48":"","text49":null,"text5":"","text50":"","text52":null,"text6":"","text7":"","text8":"","text9":""},{"id":1,"idejemplo":1,"t1":"","t11":"","t12":"","t13":"","t14":"","t15":"","t16":"","t17":"","t18":"","t2":"","t3":"","t4":"","t5":"","t6":"","t7":"","t8":"","text1":"RIVAS","text10":"","text11":"","text12":"","text13":"","text14":"","text15":"","text16":"","text17":"","text18":"","text19":"","text20":"","text21":"","text22":"","text23":"","text24":"","text25":"","text26":"","text27":"","text28":"","text29":"","text3":"","text30":"","text31":"","text32":"","text33":"","text34":"","text35":"","text36":"","text39":"","text4":"","text40":"","text41":"","text42":"","text43":"","text44":"","text45":"","text46":"","text47":"","text48":"","text49":null,"text5":"","text50":"","text52":"","text6":"","text7":"","text8":"","text9":""}]

Con esto hemos finalizado este corto pero muy educativo tutorial.



No hay comentarios:

Publicar un comentario