Buscar este blog

viernes, 29 de abril de 2016

Maven - Log4j2 with slf4j

How to use log4j2 logging over the simple log facade (slf4j).

pom.xml
<dependencies>

  <dependency>
   <groupId>org.apache.logging.log4j</groupId>
   <artifactId>log4j-slf4j-impl</artifactId>
   <version>2.5</version>
  </dependency>



  <dependency>
   <groupId>org.apache.logging.log4j</groupId>
   <artifactId>log4j-api</artifactId>
   <version>2.5</version>
  </dependency>

  <dependency>
   <groupId>org.apache.logging.log4j</groupId>
   <artifactId>log4j-core</artifactId>
   <version>2.5</version>
  </dependency>
  
 </dependencies>


You need to configure de log4j2 impl, for example, by using a log4j2.xml file:
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="INFO">
 <Appenders>
  <Console name="CONSOLE" target="SYSTEM_OUT">
   <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" />
  </Console>

  <RollingFile name="FILE" fileName="logs/app.log" filePattern="logs/app-%d{MM-dd-yyyy}.log.gz" ignoreExceptions="false">
   <PatternLayout>
    <Pattern>%d %p %c{1.} [%t] %m%n</Pattern>
   </PatternLayout>
   <TimeBasedTriggeringPolicy />
  </RollingFile>
 </Appenders>
 
 
 <Loggers>
  
  <Logger name="com.foo.Bar" level="trace" additivity="false">
      <AppenderRef ref="CONSOLE" />
   <AppenderRef ref="FILE" />
    </Logger>
 
  <Root level="INFO">
   <AppenderRef ref="CONSOLE" />
   <AppenderRef ref="FILE" />
  </Root>
 </Loggers>
</Configuration>

domingo, 24 de abril de 2016

@Firma - Firma trifase con Autofirma 1.4.2

Según se indica en su página oficial, @firma es una plataforma de validación y firma electrónica multi-PKI desarrollada por el MINHAP, y que se pone a disposición de las Administraciones Públicas, proporcionando servicios para implementar la autenticación y firma electrónica avanzada de una forma rápida y efectiva.

Originariamente @firma sólo ofrecía un applet de firma para hacer firma en cliente. De este modo las aplicaciones incluían una serie de librerías y javascript en sus páginas para ejecutar el este applet, que era el que se encargaba de acceder a los certificados del usuario.

Con la progresiva caída en desuso de los applets se ha potenciado el uso de un componente de usuario denominado Autofirma. Se trata de una aplicación independiente que debe instalar manualmente el usuario y que es quien en última instancia realiza la firma.

El caso más básico de funcionamiento consiste en que desde la página web se invoque el componente de autofirma pasándole el fichero que se desea firmar (en base 64), se firme en local, y luego se envíe de vuelta a la página. De ahí, volvería finalmente al servidor.

Este modelo de integración es válido cuando los ficheros son relativamente pequeños, pero para documentos mayores implica un tráfico de red excesivo. Además, en algunos dispositivos móviles no se permite la descarga de documentos, como por ejemplo en iOS.

Para solventar estos problemas se dispone de la opción de firma trifase. En estos casos, el documento nunca llega a salir del servidor remoto y la firma se realiza de forma conjunta entre autofirma y una nueva aplicación web denominada servidor trifase.

Cuando desde página web se invoca a autofirma (a través de invocación por protocolo), se le pasa como parámetro la URL del servidor trifase y un identificador que hace referencia al fichero remoto que se quiere firmar. Autofirma establece una comunicación con el servidor trifase, indicándole este identificador y se intercambian mensajes para componer una firma e incrustrarla en en el documento resultante.

En la siguiente imagen se muestra el diagrama general de funcionamiento.


El componente autofirma y el server trifase se pueden descargar de la página de la forma del CTT, http://forja-ctt.administracionelectronica.gob.es/web/clienteafirma. Desde ahí se puede ir a la página de la forja.

Además, el código fuente del server trifase (como el del resto del proyecto) se puede consultar en su github https://github.com/ctt-gob-es/clienteafirma.

Para hacer pruebas lo mejor es descargar el server trifase y modificar su config.properties para que emplee acceda a una ruta de disco, que servirá de entrada y salida de los ficheros firmados. El fichero de configuración está en WEB-INF/classes/config.properties:
# Origenes permitidos
Access-Control-Allow-Origin=*

# Clase DocumentManager
#document.manager=es.gob.afirma.triphase.server.document.SelfishDocumentManager
document.manager=es.gob.afirma.triphase.server.document.FileSystemDocumentManager

# Instalar provedor de XMLdSig alternativo
alternative.xmldsig=false

# Configuracion de la clase FileSystemDocumentManager
indir=D:/tmp/afirma/entrada
outdir=D:/tmp/afirma/salida
overwrite=true

Luego basta con desplegarlo tal cual en un servidor de aplicaciones, por ejemplo tomcat.

Al descargar el componente de autofirma también se incluye una página para probar todas las opciones disponibles de firma. He cogido esa página y la he recortado para dejar únicamente la parte de firma trifase.
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html> <!-- Ejemplo basico de lanzador de la aplicacion -->
  <head>
 <title>Ejemplo de despliegue del MiniApplet @firma</title>
    <meta http-equiv="Content-Type" content="text/html;charset=utf-8" >
 <script type="text/javascript" src="miniapplet.js"></script>
 <script type="text/javascript">

  function doSign() {
   try {
    MiniApplet.sign(
     document.getElementById("idFicheroServidor").value,
     "SHA512withRSA",
     "PAdEStri",
     document.getElementById("params").value,
     showResultCallback,
     showErrorCallback);
    
   } catch(e) {
    try {
     showLog("Type: " + MiniApplet.getErrorType() + "\nMessage: " + MiniApplet.getErrorMessage());
    } catch(ex) {
     showLog("Error: " + e);
    }
   }
  }
  
  function showResultCallback(signatureB64, certificateB64) {
   showLog("Firma OK");
   document.getElementById('result').value = signatureB64;
   document.getElementById('certificate').value = certificateB64;
  }
 
  function showErrorCallback(errorType, errorMessage) {
   showLog("Type: " + errorType + "\nMessage: " + errorMessage);
  }
    
  function showAppletLog() {
   try {
    showLog(MiniApplet.getCurrentLog());
   } catch(e) {
    showLog("Type: " + MiniApplet.getErrorType() + "\nMessage: " + MiniApplet.getErrorMessage());
   }
  }
  
  function cleanDataField(dataField, textDiv) {
   textDiv.innerHTML = "";
   dataField.value = null;
  }
  
  function addExtraParam(extraParam) {
   var paramsList = document.getElementById("params");
   paramsList.value = paramsList.value + extraParam + "\n";
   document.getElementById('newParam').value = "";
  }
  
  function cleanExtraParams() {
   document.getElementById("params").value = "";
   document.getElementById('newParam').value = "";
  }
  
  function showLog(newLog) {
   document.getElementById('console').value = document.getElementById('console').value + "\n" + newLog;
  }
 </script>
  </head>
 <body>
  <script type="text/javascript">
   MiniApplet.setForceWSMode(false);
   MiniApplet.cargarAppAfirma();
  </script>

  
  <fieldset><legend>Entrada de datos</legend>
  <div>
    <span>Identificador de fichero en servidor:</span> <input id="idFicheroServidor" type="text" value="prueba.pdf">      
  </div>
  </fieldset>
  <br/>
  
  <fieldset><legend>Configuraci&oacute;n de la firma</legend>  
   <div>
     <label for="newParam">ExtraParams</label>
     <input id="newParam" type="text"><input type="button" value="Agregar" onclick="addExtraParam(document.getElementById('newParam').value);">&nbsp;
     <input type="button" value="Limpiar" onclick="cleanExtraParams();">&nbsp;
     <span>(Insertar las propiedades de una en una)</span>
     <br>
    <textarea id="params" cols="50" rows="5" readonly>serverUrl=http://localhost:8080/afirma-server-triphase-signer/SignatureService
    </textarea>
   </div>
  </fieldset>
  <br/>

  <input type="button" value="Firmar" onclick="doSign();">&nbsp;  
  <input type="button" value="Mostrar Log" onclick="showAppletLog();">
  <br/>
  
  <div>
   <span>Consola</span>
   <br>
   <textarea id="console" cols="150" rows="10">
   </textarea>
  </div>
  
  <div>
   <span>Resultado</span>
   <br>
   <textarea id="result" cols="150" rows="10">
   </textarea>
  </div>
  
  <div>
   <span>Certificado</span>
   <br>
   <textarea id="certificate" cols="150" rows="10">
   </textarea>
  </div>
 </body>
</html>

Esta página invoca la firma trifase de tipo PAdES (firma de PDF).


El único parámetro que hay que cubrir es el identificador del fichero, que será el nombre de un fichero que se encuentre en el directorio de entrada configurado dentro del server trifase.

Para ver los mensajes intercambiados entre autofirma y el server trifase se puede utilizar un filtro de log de tomcat como el describía en una entrada anterior http://trabajosdesisifo.blogspot.com.es/2016/03/tomcat-custom-request-dump-filter-log.html

sábado, 2 de abril de 2016

Camel - CXF - Spring Boot - Web Service proxy Camel Route

This is a simple Spring Boot application, based on Apache Camel which act as a web service proxy. It is useful when you want to make accessible a web service allocated in a non accessible network. You can deploy it in an intermediate machine accessible from both sides.

This is a project based on this post in Java Code Geeks, but making it bootiful ;).

The project is very simple, you only need one Camel Route:
@Configuration
public class CamelConfig {

 @Bean
 public RouteBuilder routeBuilder() {
  return new RouteBuilder() {
   @Override
   public void configure() throws Exception {
    from("cxf:bean:servicePublisher?dataFormat=POJO")
    .log(LoggingLevel.INFO,"Doing some stuff")
    .to("cxf:bean:serviceConsumer?dataFormat=POJO");
   }
  };
 }
}

The route uses two CXF Endpoints, by publishing and by consuming the same web service contract.
This is the CXF config:
@Configuration
@ImportResource("classpath:/spring/conduit-config.xml")
public class CxfConfig {

 @Value("${service.publish.localEndpoint}")
 private String localEndPoint;

 @Value("${service.client.remoteEndpoint}")
 private String remoteEndPoint;


 @Bean
 public LoggingInInterceptor loggingInInterceptor() {
  final LoggingInInterceptor loggingInInterceptor = new LoggingInInterceptor();
  loggingInInterceptor.setPrettyLogging(true);
  return loggingInInterceptor;
 }


 @Bean
 public LoggingOutInterceptor loggingOutInterceptor() {
  final LoggingOutInterceptor loggingOutInterceptor = new LoggingOutInterceptor();
  loggingOutInterceptor.setPrettyLogging(true);
  return loggingOutInterceptor;
 }


 @Bean
 public GZIPInInterceptor gzipInInterceptor() {
  return new GZIPInInterceptor();
 }


 @Bean
 public CxfEndpoint servicePublisher(final CamelContext camelContext) {
   final CxfComponent cxfComponent = new CxfComponent(camelContext);

   final CxfEndpoint serviceEndpoint = new CxfEndpoint("", cxfComponent);
   serviceEndpoint.setServiceClass(ProductPortImpl.class);
   serviceEndpoint.setAddress(localEndPoint);
   serviceEndpoint.setWsdlURL("/wsdl/service/productService.wsdl");

   serviceEndpoint.setServiceNameString("{http://ws.javacodegeeks.com/product-service}ProductService");
   serviceEndpoint.setEndpointNameString("ProductPort");

   serviceEndpoint.setDataFormat(DataFormat.POJO);

   serviceEndpoint.setLoggingFeatureEnabled(true);
   serviceEndpoint.getInInterceptors().add(gzipInInterceptor());
   serviceEndpoint.getInInterceptors().add(loggingInInterceptor());
   serviceEndpoint.getOutInterceptors().add(loggingOutInterceptor());

   return serviceEndpoint;
 }


 @Bean
 public CxfEndpoint serviceConsumer(final CamelContext camelContext) {
   final CxfComponent cxfComponent = new CxfComponent(camelContext);

   final CxfEndpoint serviceEndpoint = new CxfEndpoint("", cxfComponent);
   serviceEndpoint.setServiceClass(Product.class);
   serviceEndpoint.setAddress(remoteEndPoint);
   serviceEndpoint.setWsdlURL("/wsdl/service/productService.wsdl");

   serviceEndpoint.setServiceNameString("{http://ws.javacodegeeks.com/product-service}ProductService");
   serviceEndpoint.setEndpointNameString("ProductPort");

   serviceEndpoint.setDataFormat(DataFormat.POJO);

   serviceEndpoint.setLoggingFeatureEnabled(true);
   serviceEndpoint.getInInterceptors().add(gzipInInterceptor());
   serviceEndpoint.getInInterceptors().add(loggingInInterceptor());
   serviceEndpoint.getOutInterceptors().add(loggingOutInterceptor());

   return serviceEndpoint;
 }
}

The end points configuration is very similar, but there is two differences:
  • The publisher service class references the service impl class. The consumer service class references the service interface class.
  • The publisher  service address references the full URL in which this project will publish the service. The consumer service address references the full URL in which the true service is published (by a third app)

You also need to keep in mind the SSL, so you need a http conduit for the client end point. I tried to configure it with java-based configuration, but I couldn't, so I finally  opted for XML-based configuration.
This is the conduit-config.xml file:
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
 xmlns:cxf="http://camel.apache.org/schema/cxf"
 xmlns:sec="http://cxf.apache.org/configuration/security" 
 xmlns:http="http://cxf.apache.org/transports/http/configuration"
 xmlns:util="http://www.springframework.org/schema/util"
 xsi:schemaLocation="
     http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
     http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
              http://camel.apache.org/schema/cxf http://camel.apache.org/schema/cxf/camel-cxf.xsd
              http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd
              http://cxf.apache.org/configuration/security http://cxf.apache.org/schemas/configuration/security.xsd
              http://cxf.apache.org/transports/http/configuration http://cxf.apache.org/schemas/configuration/http-conf.xsd">

 
 <http:conduit name="{http://ws.javacodegeeks.com/product-service}ProductPort.http-conduit">
     <http:client Accept="text/xml" AcceptEncoding="gzip,deflate,sdch"  AllowChunking="true" AutoRedirect="true" CacheControl="No-Cache" ContentType="text/xml"/>
     
  <http:tlsClientParameters disableCNCheck="true">
   <sec:trustManagers>
    <sec:keyStore file="${service.client.truststore.path}" type="jks" password="${service.client.truststore.pass}" />
   </sec:trustManagers>
  </http:tlsClientParameters>
 </http:conduit>
  
</beans>

Some configuration values are environment-dependant, so I used a external properties file. This file also references a truststore used by the conduit config.
This is the PropertiesConfig file:
@Configuration
@PropertySources({
 @PropertySource("classpath:conf.properties"),
 @PropertySource("file:${propertyFile}")
})
public class PropertiesConfig {
 @Bean
 public PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
  return new PropertySourcesPlaceholderConfigurer();
 }
}

When you launch the project, you need to pass it the propertyFile key, for example as a command line argument.

The full source code is in my githubhttps://github.com/evazquezma/JEE6/tree/master/proxy-service