viernes, 24 de mayo de 2013

En esta entrada implementaremos el siguiente escenario.


Si al servicio que implementamos, desplegamos y consumimos en las entradas anteriores quisiéramos consumirlo nuevamente pero de forma segura deberíamos de hacer algunos cambios en su diseño.

Esto no lo hicimos desde el inicio para dar pie a esta entrada :-D pero la idea es que de los bindings del servicio que ya tenemos que son el HTTP, SOAP11 y SOAP12 pues tengamos 2 puertos por cada uno de ellos. Uno seguro y el otro inseguro. Ya tenemos los inseguros así que debemos crear los seguros.


Vean que cuando se usa el estándar SOAP versión 1.2 se usa el namespace referenciado por soap12 y que al inicio del WSDL podrán encontrar xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/"

Una vez hecha la modificación la imagen gráfica del WSDL visto con el eclipse tendría esta forma.



El código en la vista XML del WSDL de uno de estos puertos sería algo como esto:
    <wsdl:port name="HolamundoHttpsSoap12Endpoint"
                binding="ns:HolamundoSoap12Binding">
                <soap12:address location="https://www.example.org/" />
    </wsdl:port>



Lo importante es que el nombre del puerto lleva una s detrás del http para indicar que se usará un canal encriptado https. Y lo otro es que la dirección usa https en vez de http.

Si vuelven a revisar el WSDL que muestra el Application Server verán lo siguiente:

A partir de aquí deberíamos volver a redesplegar el servicio y probarlo a consumir de forma insegura como ya expliqué en otras entradas.
Una vez que comprueben que los cambios introducidos no han afectado el consumo inseguro pues implementaremos los siguientes pasos:

Paso 1:  iremos al dashboard del servicio y le asignaremos el menor nivel de seguridad. En el caso de la suite de wso2 es el uso del UserNameToken. O sea encriptar el canal de comunicación y usar HTTPS y enviar un token que contenga el usuario y la contraseña, ambos en texto plano, de quien desee consumir el servicio. Este usuario debe ser uno de los que están en alguno de los almacenes de usuario que tiene configurada la herramienta.

Paso 2:  usando la funcionalidad del “Try it” consumiremos el servicio seguro especificando el usuario/contraseña del admin del Application Server, que es el único usuario que tenemos hasta el momento en la herramienta.

Paso 3: implementaremos un cliente de este servicio en JAVA usando para ello el Developer Studio de WSO2.

Así que manos a la obra.

Para implementar el paso 1 nos vamos al dashboard de servicio que luce como sigue en la parte de “Quality of Service Configuration”:
Damos clic en Security y  se nos mostrará la interrogante de si habilitamos la seguridad, y seleccionamos Yes. Automáticamente se despliega el listado de posibles escenarios de seguridad. Solo les mostraré los básicos pero son 16.


Como ven marcamos el de UsernameToken y vamos al final de la página para darle al botón Next.

Luego de dar Next se nos pide especificar los grupos que tendrán permiso de acceso al servicio. Una autenticación básica para poder validar el usuario y la contraseña que se introduzcan.
Y por último damos Finish.
Y eso es todo, ya tenemos un servicio seguro sin tocar su implementación.

Cuando volvemos al Dashboard del servicio vemos que ahora solo el servicio está exponiendo un único endpoint, el seguro.
Ahora vamos a realizar el paso 2.
Le damos clic a “Try it” y veremos lo que nos sale.
En mi caso el firefox me dice que la conexión está cifrada pero no verificada, es que no confía en el certificado auto firmado que viene por defecto con la herramienta.

Luego de dar en que entendemos los riesgos y añadir la excepción vemos lo siguiente:

Como ven ya el Try it detectó la seguridad y nos da la opción de que podamos añadir el usuario y la contraseña. Introducimos admin y admin que son los valores por defecto y asignamos un nombre y un apellido y la respuesta no se hace esperar.



Ahora funciona, pero realmente funciona? Una manera de comprobarlo es ver los mensajes que se intercambian. Aquellos que sepan usar el TCPMon los invito a hacerlo pero al estar el canal encriptado poco podrán hacer a no ser que realicen una configuración y usando el par de llaves asimétricas del servidor puedan ver dentro del canal cifrado, pero a aquellos que quieran ver la estructura de los mensajes sin complicarse tanto los invito entonces a usar otra funcionalidad de la suite de WSO2 y de su framework CARBON y por lo tanto incluida en todas  las herramientas: el SOAP TRACER.

Para usarlo nos vamos a la pestaña Monitor del menú:

Y pinchamos en SOAP Tracer.
Se nos pregunta si queremos habilitar el soap tracer y le damos que YES. Veremos lo siguiente:

Ahora volvemos al  try it y consumimos de nuevo el servicio. Y cuando regresamos al soap tracer tendremos lo siguiente:

O sea el soap tracer ha capturado los mensajes, nos muestra la hora de consumo del servicio y los mensajes tanto de entrada como de salida. Es imposible mostrarlos completamente pero en el mensaje request vean como se ha añadido un encabezado “Security” y dentro del mismo un timestamp para evitar que el mensaje sea usado, reenviado, fuera de ese rango de tiempo como una medida de proteccion. También verán que hay un token Username que tiene el usuario y el password introducidos por el usuario. Cada uno de los mensajes pueden visualizarlo a pantalla completa si lo desean y ver su contenido adecuadamente.

Para finalizar esta entrada que ya se ha extendido vamos al paso 3. Consumiremos el servicio asegurado con usernametoken.

Los invito a probar el cliente creado en la otra entrada, les resultará imposible porque el endpoint que usábamos ya no existe. Si lo cambian por  el que existe ahora recibirán varios errores relacionados con el certificado que deberían tener y que no tienen. Aun.

Creamos un proyecto siguiendo los mismos pasos que vimos en la otra entrada para generar las clases del stub.
Luego de implementar el cliente y añadir las dependencias tal y como hicimos para el consumo del servicio inseguro nos sigue dando un error y está claro porque no hemos hecho nada con la seguridad.

Hasta  el momento mi implementación luce así:
package org.blogs.ejemplos.serviciosaxis2;

import java.rmi.RemoteException;
import org.apache.axis2.AxisFault;
import org.blogs.ejemplos.serviciosaxis2.HolamundoWSDLStub.Holaati;
import org.blogs.ejemplos.serviciosaxis2.HolamundoWSDLStub.HolaatiResponse;
import org.blogs.ejemplos.serviciosaxis2.HolamundoWSDLStub.Persona;

public class Cliente {

                /**

                * @param args

                */
                public static void main(String[] args) {

                               try {
                                               HolamundoWSDLStub stub = new HolamundoWSDLStub();

                                               Holaati holaati = new Holaati();
                                               Persona param = new Persona();
                                               param.setNombre("nombre1");
                                               param.setApellidos("apellido1");
                                               holaati.setPersona(param);

                                               HolaatiResponse respuesta = stub.holaati(holaati);
                                               System.out.println("La respuesta es:   " 
              + respuesta.get_return().getSaludo());

                               } catch (AxisFault e) {
                                               e.printStackTrace();
                               } catch (RemoteException e) {
                                               e.printStackTrace();
                               }
                }
}




Luego de realizar los cambios necesarios en el código nos queda la siguiente implementación con los comentario de las cosas nuevas incluidos. Pruébenlo y me dicen.
import java.rmi.RemoteException;

import org.apache.axiom.om.impl.builder.StAXOMBuilder;
import org.apache.axis2.AxisFault;
import org.apache.axis2.client.Options;
import org.apache.neethi.Policy;
import org.apache.neethi.PolicyEngine;
import org.apache.rampart.RampartMessageData;
import org.blogs.ejemplos.serviciosaxis2.HolamundoWSDLStub.Holaati;
import org.blogs.ejemplos.serviciosaxis2.HolamundoWSDLStub.HolaatiResponse;
import org.blogs.ejemplos.serviciosaxis2.HolamundoWSDLStub.Persona;

public class Cliente {

 private static Policy loadPolicy(String xmlPath) throws Exception {
  StAXOMBuilder builder = new StAXOMBuilder(xmlPath);
  return PolicyEngine.getPolicy(builder.getDocumentElement());
 }

 /**
  * @param args
  */
 public static void main(String[] args) {
  try {

   String trustStore = null;

   // ubicación del almace de llaves que contiene el certificado del
   // servidor AS
   trustStore = "c:\\keys\\cliente.jks";

   // ubicacción de la política que usa el servicio para la seguridad.
   String policyFilePath = "d:\\UTOverTransport.xml";
   // se definen las propiedades para encriptar el canal de comuncación
   // especificando el almacen de certificados de donde se extraerá el
   // certificado correspondiente al servidor
   System.setProperty("javax.net.ssl.trustStore", trustStore);
   System.setProperty("javax.net.ssl.trustStorePassword", "cliente");

   // aqui especificamos el endpoint seguro
   HolamundoWSDLStub stub = new HolamundoWSDLStub(
     "https://192.168.0.224:9445/services/HolamundoWSDL/");

   // optenemos las opciones del servicio
   Options options = stub._getServiceClient().getOptions();
   // enganchamos el modulo de rampart para la seguridad en el flujo de
   // los mensajes.
   stub._getServiceClient().engageModule("rampart");

   // pasamos el usuario y la contraseña
   options.setUserName("admin");
   options.setPassword("admin");
   // asignamos la política de servicio a las opciones del stub.
   options.setProperty(RampartMessageData.KEY_RAMPART_POLICY,
     loadPolicy(policyFilePath));
   stub._getServiceClient().setOptions(options);

   Holaati holaati = new Holaati();
   Persona param = new Persona();
   param.setNombre("nombre1");
   param.setApellidos("apellido1");
   holaati.setPersona(param);

   HolaatiResponse respuesta = stub.holaati(holaati);

   System.out.println("La respuesta es:   "
     + respuesta.get_return().getSaludo());

  } catch (AxisFault e) {
   e.printStackTrace();
  } catch (RemoteException e) {
   e.printStackTrace();
  } catch (Exception e) {
   e.printStackTrace();
  }
 }
}




Las librerías que usé fueron las siguientes(tengan en cuenta que las versiones pueden variar):

axiom-1.2.11.wso2v1.jar
axis2-1.6.1.wso2v1.jar
commons-codec-1.3.0.wso2v1.jar
commons-httpclient-3.1.0.wso2v1.jar
commons-io-2.0.0.wso2v1.jar
geronimo-stax-api_1.0_spec-1.0.1.wso2v1.jar
httpcore-4.1.0.wso2v1.jar
neethi-2.0.4.wso2v3.jar
opensaml2-2.0.0.alpha1-wso2v1.jar
org.wso2.carbon.addressing-3.2.0.jar
org.wso2.carbon.logging-3.2.0.jar
org.wso2.securevault-1.0.0.jar
rampart-core-1.6.1.wso2v1.jar
rampart-policy-1.6.1.wso2v1.jar
rampart-trust-1.6.1.wso2v1.jar
woden-1.0.0.M8-wso2v1.jar
wsdl4j-1.6.2.wso2v2.jar
wss4j-1.5.11.wso2v1.jar
XmlSchema-1.4.7.wso2v1.jar

Luego de implementar un ciclo sencillo para consumir varias veces el servicio pueden ver las siguientes estadísticas en su dashboard.


En otras entradas estaremos viendo cómo usar el SOAPUI para consumir un servicio y hacer pruebas de carga y estrés.

2 comentarios:

  1. Hola Jorge te saluda Julio, cuando ejecuto el cliente me sale este mensaje de checkMustUnderstand. y en el soaptracer sale en blanco. me puedes ayudar porfavor?


    org.apache.axis2.AxisFault: Must Understand check failed for header http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd : Security
    at org.apache.axis2.engine.AxisEngine.checkMustUnderstand(AxisEngine.java:104)
    at org.apache.axis2.engine.AxisEngine.receive(AxisEngine.java:170)
    at org.apache.axis2.description.OutInAxisOperationClient.handleResponse(OutInAxisOperation.java:356)
    at org.apache.axis2.description.OutInAxisOperationClient.send(OutInAxisOperation.java:413)
    at org.apache.axis2.description.OutInAxisOperationClient.executeImpl(OutInAxisOperation.java:224)
    at org.apache.axis2.client.OperationClient.execute(OperationClient.java:149)
    at org.wso2.www.types.HelloServiceStub.greet(HelloServiceStub.java:191)
    at org.wso2.www.types.Cliente.main(Cliente.java:64)

    ResponderEliminar
    Respuestas
    1. Hola Julio, en una entrada que subiré dentro de poco pondré los proyectos que he actualizado para este escenario. El proyecto que genera el aar para desplegar en el WSO2 AS y el proyecto que tiene el cliente para axis2 con seguridad UT. En otra entrada postearé lo mismo pero para JAX-WS. Debe ser en lo que queda de semana. Disculpa la demora en responder.

      Eliminar